@flash-ai-team/flash-test-framework 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/keywords/AIWebUI.d.ts +5 -0
- package/dist/keywords/AIWebUI.js +142 -5
- package/dist/keywords/WebUI.d.ts +30 -0
- package/dist/keywords/WebUI.js +90 -0
- package/dist/reporting/ReporterUtils.js +5 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.el = exports.TestObject = exports.KeywordContext = exports.Keyword = exports.AIWeb = exports.Web = void 0;
|
|
3
|
+
exports.TestCase = exports.el = exports.TestObject = exports.KeywordContext = exports.Keyword = exports.AIWeb = exports.Web = void 0;
|
|
4
4
|
var WebUI_1 = require("./keywords/WebUI");
|
|
5
5
|
Object.defineProperty(exports, "Web", { enumerable: true, get: function () { return WebUI_1.Web; } });
|
|
6
6
|
var AIWebUI_1 = require("./keywords/AIWebUI");
|
|
@@ -11,3 +11,5 @@ Object.defineProperty(exports, "KeywordContext", { enumerable: true, get: functi
|
|
|
11
11
|
var ObjectRepository_1 = require("./core/ObjectRepository");
|
|
12
12
|
Object.defineProperty(exports, "TestObject", { enumerable: true, get: function () { return ObjectRepository_1.TestObject; } });
|
|
13
13
|
Object.defineProperty(exports, "el", { enumerable: true, get: function () { return ObjectRepository_1.el; } });
|
|
14
|
+
var TestDecorators_1 = require("./core/TestDecorators");
|
|
15
|
+
Object.defineProperty(exports, "TestCase", { enumerable: true, get: function () { return TestDecorators_1.TestCase; } });
|
|
@@ -12,6 +12,11 @@ export declare class AIWeb {
|
|
|
12
12
|
static click(description: string): Promise<void>;
|
|
13
13
|
static setText(description: string, text: string): Promise<void>;
|
|
14
14
|
static verifyText(description: string, expectedText: string): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Specialized handler for reCAPTCHA.
|
|
17
|
+
* Searches for the reCAPTCHA iframe and clicks the checkbox.
|
|
18
|
+
*/
|
|
19
|
+
static clickReCaptcha(): Promise<void>;
|
|
15
20
|
/**
|
|
16
21
|
* Executes manual test steps by converting them to WebUI keyword calls using AI.
|
|
17
22
|
* @param steps - Natural language test steps (one per line or paragraph).
|
package/dist/keywords/AIWebUI.js
CHANGED
|
@@ -152,6 +152,36 @@ class AIWeb {
|
|
|
152
152
|
throw new Error(`Expected text "${expectedText}" not found in element "${description}". Found: "${text}"`);
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Specialized handler for reCAPTCHA.
|
|
157
|
+
* Searches for the reCAPTCHA iframe and clicks the checkbox.
|
|
158
|
+
*/
|
|
159
|
+
static async clickReCaptcha() {
|
|
160
|
+
console.log("Attempting to find and click reCAPTCHA...");
|
|
161
|
+
const frames = this.page.frames();
|
|
162
|
+
let captchaFrame = null;
|
|
163
|
+
// Find the reCAPTCHA iframe
|
|
164
|
+
for (const frame of frames) {
|
|
165
|
+
if (frame.url().includes('google.com/recaptcha/api2/anchor')) {
|
|
166
|
+
captchaFrame = frame;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!captchaFrame) {
|
|
171
|
+
console.warn("reCAPTCHA iframe not found among " + frames.length + " frames.");
|
|
172
|
+
// Try waiting? fallback
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const anchor = captchaFrame.locator('#recaptcha-anchor, .recaptcha-checkbox-border').first();
|
|
176
|
+
try {
|
|
177
|
+
await anchor.waitFor({ state: 'visible', timeout: 5000 });
|
|
178
|
+
await anchor.click();
|
|
179
|
+
console.log("Clicked reCAPTCHA anchor.");
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
console.error("Failed to click reCAPTCHA anchor", e);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
155
185
|
/**
|
|
156
186
|
* Executes manual test steps by converting them to WebUI keyword calls using AI.
|
|
157
187
|
* @param steps - Natural language test steps (one per line or paragraph).
|
|
@@ -164,14 +194,56 @@ class AIWeb {
|
|
|
164
194
|
// Remove numbering (e.g., "1. ")
|
|
165
195
|
const cleanLine = line.replace(/^\d+\.\s*/, '').trim();
|
|
166
196
|
console.log(`Processing step: "${cleanLine}"`);
|
|
197
|
+
// 8. Wait for "..." to disappear (Moved to top priority to avoid conflicts)
|
|
198
|
+
const waitDisappearMatch = cleanLine.match(/^Wait for "([^"]+)"(?: .+)? (?:to )?disappear$/i);
|
|
199
|
+
if (waitDisappearMatch) {
|
|
200
|
+
const targetDesc = waitDisappearMatch[1];
|
|
201
|
+
const selector = `text="${targetDesc}" >> nth=0`;
|
|
202
|
+
await WebUI_1.Web.waitForElementNotVisible((0, ObjectRepository_1.el)(selector, targetDesc));
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
// 10. Verify that element "..." contains text "..." is also "..."
|
|
206
|
+
const tripleCheckMatch = cleanLine.match(/^Verify that element "([^"]+)" contains (?:text )?"([^"]+)" is also "([^"]+)"$/i);
|
|
207
|
+
if (tripleCheckMatch) {
|
|
208
|
+
const containerDesc = tripleCheckMatch[1];
|
|
209
|
+
const text1 = tripleCheckMatch[2];
|
|
210
|
+
const text2 = tripleCheckMatch[3];
|
|
211
|
+
const selector = `*:has-text("${containerDesc}"):has-text("${text1}"):has-text("${text2}") >> nth=-1`;
|
|
212
|
+
await WebUI_1.Web.verifyElementPresent((0, ObjectRepository_1.el)(selector, `Element '${containerDesc}' containing '${text1}' and '${text2}'`));
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
// 9. Verify that element "..." contains text "..."
|
|
216
|
+
const elementContainsMatch = cleanLine.match(/^Verify that element "([^"]+)" contains (?:text )?"([^"]+)"$/i);
|
|
217
|
+
if (elementContainsMatch) {
|
|
218
|
+
const containerDesc = elementContainsMatch[1];
|
|
219
|
+
const containedText = elementContainsMatch[2];
|
|
220
|
+
// Select the deepest element (*) that contains BOTH strings.
|
|
221
|
+
// nth=-1 selects the last one, which is typically the most specific common ancestor in the DOM tree order.
|
|
222
|
+
const selector = `*:has-text("${containerDesc}"):has-text("${containedText}") >> nth=-1`;
|
|
223
|
+
await WebUI_1.Web.verifyElementPresent((0, ObjectRepository_1.el)(selector, `Element '${containerDesc}' with text '${containedText}'`));
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
// SPECIAL CASE: reCAPTCHA
|
|
227
|
+
if (cleanLine.toLowerCase().includes("i'm not a robot") || cleanLine.toLowerCase().includes("i am not robot")) {
|
|
228
|
+
await this.clickReCaptcha();
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
// 8a. Wait for "..." to appear
|
|
232
|
+
const waitAppearMatch = cleanLine.match(/^Wait for "([^"]+)"(?: .+)? (?:to )?appear$/i);
|
|
233
|
+
if (waitAppearMatch) {
|
|
234
|
+
const targetDesc = waitAppearMatch[1];
|
|
235
|
+
const selector = `text="${targetDesc}" >> nth=0`;
|
|
236
|
+
await WebUI_1.Web.waitForElementVisible((0, ObjectRepository_1.el)(selector, targetDesc));
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
167
239
|
// 1. Navigate to "url"
|
|
168
240
|
const navigateMatch = cleanLine.match(/^Navigate to "([^"]+)"$/i);
|
|
169
241
|
if (navigateMatch) {
|
|
170
242
|
await WebUI_1.Web.navigateToUrl(navigateMatch[1]);
|
|
171
243
|
continue;
|
|
172
244
|
}
|
|
173
|
-
// 2. Enter "text" into ...
|
|
174
|
-
const enterMatch = cleanLine.match(/^Enter "([^"]+)" into (?:the )?(.+)$/i);
|
|
245
|
+
// 2. Enter "text" into ... / Type "text" into ...
|
|
246
|
+
const enterMatch = cleanLine.match(/^(?:Enter|Type) "([^"]+)" into (?:the )?(.+)$/i);
|
|
175
247
|
if (enterMatch) {
|
|
176
248
|
const text = enterMatch[1];
|
|
177
249
|
let targetDesc = enterMatch[2];
|
|
@@ -230,9 +302,10 @@ class AIWeb {
|
|
|
230
302
|
continue;
|
|
231
303
|
}
|
|
232
304
|
// 5. Scroll to ...
|
|
305
|
+
const quotedScrollMatch = cleanLine.match(/^Scroll to (?:the )?"([^"]+)"/i);
|
|
233
306
|
const scrollMatch = cleanLine.match(/^Scroll to (?:the )?(.+)$/i);
|
|
234
|
-
if (scrollMatch) {
|
|
235
|
-
let targetDesc = scrollMatch[1];
|
|
307
|
+
if (quotedScrollMatch || scrollMatch) {
|
|
308
|
+
let targetDesc = quotedScrollMatch ? quotedScrollMatch[1] : scrollMatch[1];
|
|
236
309
|
targetDesc = targetDesc.replace(/ button$/i, '').replace(/ link$/i, '').replace(/ marker$/i, '').trim();
|
|
237
310
|
const selector = `text="${targetDesc}" >> nth=0`;
|
|
238
311
|
await WebUI_1.Web.scrollToElement((0, ObjectRepository_1.el)(selector, targetDesc));
|
|
@@ -242,7 +315,71 @@ class AIWeb {
|
|
|
242
315
|
const verifyTextMatch = cleanLine.match(/^Verify that the text "([^"]+)" is present$/i);
|
|
243
316
|
if (verifyTextMatch) {
|
|
244
317
|
const expectedText = verifyTextMatch[1];
|
|
245
|
-
await WebUI_1.Web.verifyElementPresent((0, ObjectRepository_1.el)(`text="${expectedText}"`, `Text '${expectedText}'`));
|
|
318
|
+
await WebUI_1.Web.verifyElementPresent((0, ObjectRepository_1.el)(`text="${expectedText}" >> nth=0`, `Text '${expectedText}'`));
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
// 7. Delay for ...
|
|
322
|
+
const delayMatch = cleanLine.match(/^Delay for ([\d.]+) seconds?$/i);
|
|
323
|
+
if (delayMatch) {
|
|
324
|
+
const seconds = parseFloat(delayMatch[1]);
|
|
325
|
+
await WebUI_1.Web.delay(seconds);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
// Verify page url is "..."
|
|
329
|
+
const verifyUrlMatch = cleanLine.match(/^Verify page url is "([^"]+)"$/i);
|
|
330
|
+
if (verifyUrlMatch) {
|
|
331
|
+
const url = verifyUrlMatch[1];
|
|
332
|
+
await WebUI_1.Web.verifyUrl(url);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
// Maximize Window
|
|
336
|
+
if (cleanLine.match(/^Maximize window$/i)) {
|
|
337
|
+
await WebUI_1.Web.maximizeWindow();
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
// Minimize Window
|
|
341
|
+
if (cleanLine.match(/^Minimize window$/i)) {
|
|
342
|
+
await WebUI_1.Web.minimizeWindow();
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
// Wait for Angular to load
|
|
346
|
+
if (cleanLine.match(/^Wait for Angular to load$/i)) {
|
|
347
|
+
await WebUI_1.Web.waitForAngularLoad();
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
// Check "..."
|
|
351
|
+
const checkMatch = cleanLine.match(/^Check "([^"]+)"$/i);
|
|
352
|
+
if (checkMatch) {
|
|
353
|
+
const targetDesc = checkMatch[1];
|
|
354
|
+
// Try finding by label or just text.
|
|
355
|
+
// For Material checkbox, label usually contains the text.
|
|
356
|
+
const selector = `text="${targetDesc}" >> nth=0`;
|
|
357
|
+
await WebUI_1.Web.check((0, ObjectRepository_1.el)(selector, targetDesc));
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
// Uncheck "..."
|
|
361
|
+
const uncheckMatch = cleanLine.match(/^Uncheck "([^"]+)"$/i);
|
|
362
|
+
if (uncheckMatch) {
|
|
363
|
+
const targetDesc = uncheckMatch[1];
|
|
364
|
+
const selector = `text="${targetDesc}" >> nth=0`;
|
|
365
|
+
await WebUI_1.Web.uncheck((0, ObjectRepository_1.el)(selector, targetDesc));
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
// Verify that element "..." is checked/unchecked
|
|
369
|
+
const verifyCheckedMatch = cleanLine.match(/^Verify that element "([^"]+)" is (checked|unchecked)$/i);
|
|
370
|
+
if (verifyCheckedMatch) {
|
|
371
|
+
const targetDesc = verifyCheckedMatch[1];
|
|
372
|
+
const state = verifyCheckedMatch[2].toLowerCase();
|
|
373
|
+
const selector = `text="${targetDesc}" >> nth=0`;
|
|
374
|
+
await WebUI_1.Web.verifyElementChecked((0, ObjectRepository_1.el)(selector, targetDesc), state === 'checked');
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
// Set window size to WxH
|
|
378
|
+
const setSizeMatch = cleanLine.match(/^Set window size to (\d+)x(\d+)$/i);
|
|
379
|
+
if (setSizeMatch) {
|
|
380
|
+
const width = parseInt(setSizeMatch[1]);
|
|
381
|
+
const height = parseInt(setSizeMatch[2]);
|
|
382
|
+
await WebUI_1.Web.setWindowSize(width, height);
|
|
246
383
|
continue;
|
|
247
384
|
}
|
|
248
385
|
console.warn(`Could not parse step: "${cleanLine}"`);
|
package/dist/keywords/WebUI.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export declare class Web {
|
|
|
7
7
|
* @param url - The URL to navigate to.
|
|
8
8
|
*/
|
|
9
9
|
static navigateToUrl(url: string): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Verifies that the current page URL matches the expected URL.
|
|
12
|
+
* @param url - The expected URL.
|
|
13
|
+
* @param timeout - The maximum time to wait in milliseconds (default: 5000).
|
|
14
|
+
*/
|
|
15
|
+
static verifyUrl(url: string, timeout?: number): Promise<void>;
|
|
10
16
|
/**
|
|
11
17
|
* Clicks on the specified element.
|
|
12
18
|
* @param to - The TestObject representing the element.
|
|
@@ -123,6 +129,12 @@ export declare class Web {
|
|
|
123
129
|
* @param expectedValue - The expected attribute value.
|
|
124
130
|
*/
|
|
125
131
|
static verifyElementAttributeValue(to: TestObject, attribute: string, expectedValue: string): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Verifies that the element is checked or unchecked.
|
|
134
|
+
* @param to - The TestObject representing the checkbox/radio.
|
|
135
|
+
* @param checked - True to verify checked, false to verify unchecked (default: true).
|
|
136
|
+
*/
|
|
137
|
+
static verifyElementChecked(to: TestObject, checked?: boolean): Promise<void>;
|
|
126
138
|
/**
|
|
127
139
|
* Waits for the specified element to be visible.
|
|
128
140
|
* @param to - The TestObject representing the element.
|
|
@@ -163,4 +175,22 @@ export declare class Web {
|
|
|
163
175
|
* @param to - The TestObject representing the element.
|
|
164
176
|
*/
|
|
165
177
|
static scrollToElement(to: TestObject): Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Maximizes the browser window (sets viewport to 1920x1080).
|
|
180
|
+
*/
|
|
181
|
+
static maximizeWindow(): Promise<void>;
|
|
182
|
+
/**
|
|
183
|
+
* Minimizes the browser window (sets viewport to a small size).
|
|
184
|
+
*/
|
|
185
|
+
static minimizeWindow(): Promise<void>;
|
|
186
|
+
/**
|
|
187
|
+
* Sets the browser window size.
|
|
188
|
+
* @param width - The width in pixels.
|
|
189
|
+
* @param height - The height in pixels.
|
|
190
|
+
*/
|
|
191
|
+
static setWindowSize(width: number, height: number): Promise<void>;
|
|
192
|
+
/**
|
|
193
|
+
* Waits for Angular to finish rendering.
|
|
194
|
+
*/
|
|
195
|
+
static waitForAngularLoad(): Promise<void>;
|
|
166
196
|
}
|
package/dist/keywords/WebUI.js
CHANGED
|
@@ -31,6 +31,14 @@ class Web {
|
|
|
31
31
|
static async navigateToUrl(url) {
|
|
32
32
|
await this.page.goto(url);
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Verifies that the current page URL matches the expected URL.
|
|
36
|
+
* @param url - The expected URL.
|
|
37
|
+
* @param timeout - The maximum time to wait in milliseconds (default: 5000).
|
|
38
|
+
*/
|
|
39
|
+
static async verifyUrl(url, timeout = 5000) {
|
|
40
|
+
await (0, test_1.expect)(this.page).toHaveURL(url, { timeout });
|
|
41
|
+
}
|
|
34
42
|
/**
|
|
35
43
|
* Clicks on the specified element.
|
|
36
44
|
* @param to - The TestObject representing the element.
|
|
@@ -262,6 +270,19 @@ class Web {
|
|
|
262
270
|
static async verifyElementAttributeValue(to, attribute, expectedValue) {
|
|
263
271
|
await (0, test_1.expect)(this.getLocator(to)).toHaveAttribute(attribute, expectedValue);
|
|
264
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Verifies that the element is checked or unchecked.
|
|
275
|
+
* @param to - The TestObject representing the checkbox/radio.
|
|
276
|
+
* @param checked - True to verify checked, false to verify unchecked (default: true).
|
|
277
|
+
*/
|
|
278
|
+
static async verifyElementChecked(to, checked = true) {
|
|
279
|
+
if (checked) {
|
|
280
|
+
await (0, test_1.expect)(this.getLocator(to)).toBeChecked();
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
await (0, test_1.expect)(this.getLocator(to)).not.toBeChecked();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
265
286
|
// --- Wait Keywords ---
|
|
266
287
|
/**
|
|
267
288
|
* Waits for the specified element to be visible.
|
|
@@ -324,6 +345,39 @@ class Web {
|
|
|
324
345
|
static async scrollToElement(to) {
|
|
325
346
|
await this.getLocator(to).scrollIntoViewIfNeeded();
|
|
326
347
|
}
|
|
348
|
+
// --- Window Keywords ---
|
|
349
|
+
/**
|
|
350
|
+
* Maximizes the browser window (sets viewport to 1920x1080).
|
|
351
|
+
*/
|
|
352
|
+
static async maximizeWindow() {
|
|
353
|
+
await this.page.setViewportSize({ width: 1920, height: 1080 });
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Minimizes the browser window (sets viewport to a small size).
|
|
357
|
+
*/
|
|
358
|
+
static async minimizeWindow() {
|
|
359
|
+
await this.page.setViewportSize({ width: 500, height: 400 });
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Sets the browser window size.
|
|
363
|
+
* @param width - The width in pixels.
|
|
364
|
+
* @param height - The height in pixels.
|
|
365
|
+
*/
|
|
366
|
+
static async setWindowSize(width, height) {
|
|
367
|
+
await this.page.setViewportSize({ width, height });
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Waits for Angular to finish rendering.
|
|
371
|
+
*/
|
|
372
|
+
static async waitForAngularLoad() {
|
|
373
|
+
await this.page.evaluate(async () => {
|
|
374
|
+
// @ts-ignore
|
|
375
|
+
if (window.getAllAngularTestabilities) {
|
|
376
|
+
// @ts-ignore
|
|
377
|
+
await Promise.all(window.getAllAngularTestabilities().map(t => new Promise(resolve => t.whenStable(resolve))));
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
327
381
|
}
|
|
328
382
|
exports.Web = Web;
|
|
329
383
|
__decorate([
|
|
@@ -332,6 +386,12 @@ __decorate([
|
|
|
332
386
|
__metadata("design:paramtypes", [String]),
|
|
333
387
|
__metadata("design:returntype", Promise)
|
|
334
388
|
], Web, "navigateToUrl", null);
|
|
389
|
+
__decorate([
|
|
390
|
+
(0, Keyword_1.Keyword)("Verify Url"),
|
|
391
|
+
__metadata("design:type", Function),
|
|
392
|
+
__metadata("design:paramtypes", [String, Number]),
|
|
393
|
+
__metadata("design:returntype", Promise)
|
|
394
|
+
], Web, "verifyUrl", null);
|
|
335
395
|
__decorate([
|
|
336
396
|
(0, Keyword_1.Keyword)("Click"),
|
|
337
397
|
__metadata("design:type", Function),
|
|
@@ -458,6 +518,12 @@ __decorate([
|
|
|
458
518
|
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, String, String]),
|
|
459
519
|
__metadata("design:returntype", Promise)
|
|
460
520
|
], Web, "verifyElementAttributeValue", null);
|
|
521
|
+
__decorate([
|
|
522
|
+
(0, Keyword_1.Keyword)("Verify Element Checked"),
|
|
523
|
+
__metadata("design:type", Function),
|
|
524
|
+
__metadata("design:paramtypes", [ObjectRepository_1.TestObject, Boolean]),
|
|
525
|
+
__metadata("design:returntype", Promise)
|
|
526
|
+
], Web, "verifyElementChecked", null);
|
|
461
527
|
__decorate([
|
|
462
528
|
(0, Keyword_1.Keyword)("Wait For Element Visible"),
|
|
463
529
|
__metadata("design:type", Function),
|
|
@@ -500,3 +566,27 @@ __decorate([
|
|
|
500
566
|
__metadata("design:paramtypes", [ObjectRepository_1.TestObject]),
|
|
501
567
|
__metadata("design:returntype", Promise)
|
|
502
568
|
], Web, "scrollToElement", null);
|
|
569
|
+
__decorate([
|
|
570
|
+
(0, Keyword_1.Keyword)("Maximize Window"),
|
|
571
|
+
__metadata("design:type", Function),
|
|
572
|
+
__metadata("design:paramtypes", []),
|
|
573
|
+
__metadata("design:returntype", Promise)
|
|
574
|
+
], Web, "maximizeWindow", null);
|
|
575
|
+
__decorate([
|
|
576
|
+
(0, Keyword_1.Keyword)("Minimize Window"),
|
|
577
|
+
__metadata("design:type", Function),
|
|
578
|
+
__metadata("design:paramtypes", []),
|
|
579
|
+
__metadata("design:returntype", Promise)
|
|
580
|
+
], Web, "minimizeWindow", null);
|
|
581
|
+
__decorate([
|
|
582
|
+
(0, Keyword_1.Keyword)("Set Window Size"),
|
|
583
|
+
__metadata("design:type", Function),
|
|
584
|
+
__metadata("design:paramtypes", [Number, Number]),
|
|
585
|
+
__metadata("design:returntype", Promise)
|
|
586
|
+
], Web, "setWindowSize", null);
|
|
587
|
+
__decorate([
|
|
588
|
+
(0, Keyword_1.Keyword)("Wait For Angular Load"),
|
|
589
|
+
__metadata("design:type", Function),
|
|
590
|
+
__metadata("design:paramtypes", []),
|
|
591
|
+
__metadata("design:returntype", Promise)
|
|
592
|
+
], Web, "waitForAngularLoad", null);
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.getReportFolder = getReportFolder;
|
|
37
37
|
exports.resetReportFolderCache = resetReportFolderCache;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
38
39
|
const path = __importStar(require("path"));
|
|
39
40
|
let cachedReportFolder = null;
|
|
40
41
|
function getReportFolder(suite) {
|
|
@@ -56,6 +57,10 @@ function getReportFolder(suite) {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
cachedReportFolder = path.join(process.cwd(), 'reports', suiteName, `test_${timestamp}`);
|
|
60
|
+
// Ensure the folder exists
|
|
61
|
+
if (!fs.existsSync(cachedReportFolder)) {
|
|
62
|
+
fs.mkdirSync(cachedReportFolder, { recursive: true });
|
|
63
|
+
}
|
|
59
64
|
console.log(`[ReporterUtils] Calculated report folder: ${cachedReportFolder}`);
|
|
60
65
|
return cachedReportFolder;
|
|
61
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flash-ai-team/flash-test-framework",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "A powerful keyword-driven automation framework built on top of Playwright and TypeScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"playwright",
|
|
@@ -60,4 +60,4 @@
|
|
|
60
60
|
"@types/nodemailer": "^7.0.4",
|
|
61
61
|
"typescript": "^5.9.3"
|
|
62
62
|
}
|
|
63
|
-
}
|
|
63
|
+
}
|