@flash-ai-team/flash-test-framework 0.0.16 → 0.0.18

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 CHANGED
@@ -134,6 +134,12 @@ test.describe('AI Scenario', () => {
134
134
 
135
135
  ## Keywords
136
136
 
137
+ ### Core Helpers
138
+
139
+ * `el(selector, description?)`: Creates a Test Object.
140
+ * `selector` (string): The CSS selector, XPath, or Playwright selector (e.g. `text=Submit`, `role=button[name="Login"]`).
141
+ * `description` (string): Optional description for reporting logs.
142
+
137
143
  ### Web
138
144
 
139
145
  #### Navigation
@@ -168,12 +174,15 @@ test.describe('AI Scenario', () => {
168
174
  * `verifyElementText(testObject, expectedText)`: Assert element text matches.
169
175
  * `verifyElementAttributeValue(testObject, attribute, value)`: Assert element attribute matches.
170
176
  * `verifyElementChecked(testObject, checked?)`: Assert element checked state.
177
+ * `verifyElementClickable(testObject, timeout?)`: Assert element is visible and enabled.
171
178
  * `verifyTextPresent(text)`: Assert text exists on the page.
172
179
 
173
180
  #### Synchonization
174
181
  * `waitForElementVisible(testObject, timeout?)`: Wait for element to be visible.
175
182
  * `waitForElementNotVisible(testObject, timeout?)`: Wait for element to disappear.
176
183
  * `waitForElementClickable(testObject, timeout?)`: Wait for element to be clickable.
184
+ * `waitForPageLoad(timeout?, state?)`: Wait for page load state ('load', 'domcontentloaded', 'networkidle').
185
+ * `waitForNextPageLoaded(timeout?, waitUntil?)`: Wait for next navigation to complete.
177
186
  * `waitForAngularLoad()`: Wait for Angular stability (if applicable).
178
187
  * `delay(seconds)`: Hard wait (use sparingly).
179
188
 
@@ -1,12 +1,13 @@
1
+ import { Locator } from '@playwright/test';
1
2
  /**
2
3
  * Represents a Test Object wrapper around Playwright Locators
3
4
  */
4
5
  export declare class TestObject {
5
- selector: string;
6
+ selector: string | Locator;
6
7
  description: string;
7
- constructor(selector: string, description?: string);
8
+ constructor(selector: string | Locator, description?: string);
8
9
  }
9
10
  /**
10
11
  * Helper to create Test Objects easily
11
12
  */
12
- export declare function el(selector: string, description?: string): TestObject;
13
+ export declare function el(selector: string | Locator, description?: string): TestObject;
@@ -24,6 +24,10 @@ export declare class Web {
24
24
  */
25
25
  static click(to: TestObject, options?: {
26
26
  description?: string;
27
+ position?: {
28
+ x: number;
29
+ y: number;
30
+ };
27
31
  }): Promise<void>;
28
32
  /**
29
33
  * Sets the text of an input element.
@@ -33,6 +37,14 @@ export declare class Web {
33
37
  static setText(to: TestObject, text: string, options?: {
34
38
  description?: string;
35
39
  }): Promise<void>;
40
+ /**
41
+ * Sets the text of a password input element.
42
+ * @param to - The TestObject representing the input element.
43
+ * @param text - The text to set.
44
+ */
45
+ static setPassword(to: TestObject, text: string, options?: {
46
+ description?: string;
47
+ }): Promise<void>;
36
48
  /**
37
49
  * Searches for the specified text using heuristic selectors.
38
50
  * @param text - The text to search for.
@@ -68,6 +80,10 @@ export declare class Web {
68
80
  */
69
81
  static doubleClick(to: TestObject, options?: {
70
82
  description?: string;
83
+ position?: {
84
+ x: number;
85
+ y: number;
86
+ };
71
87
  }): Promise<void>;
72
88
  /**
73
89
  * Right clicks (context click) on the specified element.
@@ -166,6 +182,12 @@ export declare class Web {
166
182
  * @param checked - True to verify checked, false to verify unchecked (default: true).
167
183
  */
168
184
  static verifyElementChecked(to: TestObject, checked?: boolean): Promise<void>;
185
+ /**
186
+ * Verifies that the element is clickable (visible and enabled).
187
+ * @param to - The TestObject representing the element.
188
+ * @param timeout - The maximum time to wait in milliseconds (default: 5000).
189
+ */
190
+ static verifyElementClickable(to: TestObject, timeout?: number): Promise<void>;
169
191
  /**
170
192
  * Waits for the specified element to be visible.
171
193
  * @param to - The TestObject representing the element.
@@ -184,6 +206,18 @@ export declare class Web {
184
206
  * @param timeout - The maximum time to wait in milliseconds (default: 5000).
185
207
  */
186
208
  static waitForElementNotVisible(to: TestObject, timeout?: number): Promise<void>;
209
+ /**
210
+ * Waits for the page to load.
211
+ * @param timeout - The maximum time to wait in milliseconds (default: 30000).
212
+ * @param state - The load state to wait for ('load' | 'domcontentloaded' | 'networkidle'). Default is 'load'.
213
+ */
214
+ static waitForPageLoad(timeout?: number, state?: 'load' | 'domcontentloaded' | 'networkidle'): Promise<void>;
215
+ /**
216
+ * Waits for the next navigation to complete.
217
+ * @param timeout - The maximum time to wait in milliseconds (default: 30000).
218
+ * @param waitUntil - The event to wait for ('load' | 'domcontentloaded' | 'networkidle' | 'commit'). Default is 'load'.
219
+ */
220
+ static waitForNextPageLoaded(timeout?: number, waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | 'commit'): Promise<void>;
187
221
  /**
188
222
  * Delays execution for a specified number of seconds.
189
223
  * @param seconds - The number of seconds to wait.
@@ -224,4 +258,9 @@ export declare class Web {
224
258
  * Waits for Angular to finish rendering.
225
259
  */
226
260
  static waitForAngularLoad(): Promise<void>;
261
+ /**
262
+ * Switches to the window/tab with the specified title.
263
+ * @param title - The title of the window to switch to.
264
+ */
265
+ static switchToTabTitle(title: string): Promise<void>;
227
266
  }
@@ -22,7 +22,10 @@ class Web {
22
22
  return Keyword_1.KeywordContext.page;
23
23
  }
24
24
  static getLocator(to) {
25
- return this.page.locator(to.selector);
25
+ if (typeof to.selector === 'string') {
26
+ return this.page.locator(to.selector);
27
+ }
28
+ return to.selector;
26
29
  }
27
30
  /**
28
31
  * Navigates to the specified URL.
@@ -45,7 +48,7 @@ class Web {
45
48
  * @param options - Optional parameters (description).
46
49
  */
47
50
  static async click(to, options) {
48
- await this.getLocator(to).click();
51
+ await this.getLocator(to).click({ position: options?.position });
49
52
  }
50
53
  /**
51
54
  * Sets the text of an input element.
@@ -55,6 +58,14 @@ class Web {
55
58
  static async setText(to, text, options) {
56
59
  await this.getLocator(to).fill(text);
57
60
  }
61
+ /**
62
+ * Sets the text of a password input element.
63
+ * @param to - The TestObject representing the input element.
64
+ * @param text - The text to set.
65
+ */
66
+ static async setPassword(to, text, options) {
67
+ await this.getLocator(to).fill(text);
68
+ }
58
69
  /**
59
70
  * Searches for the specified text using heuristic selectors.
60
71
  * @param text - The text to search for.
@@ -102,7 +113,7 @@ class Web {
102
113
  * @param to - The TestObject representing the element.
103
114
  */
104
115
  static async doubleClick(to, options) {
105
- await this.getLocator(to).dblclick();
116
+ await this.getLocator(to).dblclick({ position: options?.position });
106
117
  }
107
118
  /**
108
119
  * Right clicks (context click) on the specified element.
@@ -292,6 +303,16 @@ class Web {
292
303
  await (0, test_1.expect)(this.getLocator(to)).not.toBeChecked();
293
304
  }
294
305
  }
306
+ /**
307
+ * Verifies that the element is clickable (visible and enabled).
308
+ * @param to - The TestObject representing the element.
309
+ * @param timeout - The maximum time to wait in milliseconds (default: 5000).
310
+ */
311
+ static async verifyElementClickable(to, timeout = 5000) {
312
+ const locator = this.getLocator(to);
313
+ await (0, test_1.expect)(locator).toBeVisible({ timeout });
314
+ await (0, test_1.expect)(locator).toBeEnabled({ timeout });
315
+ }
295
316
  // --- Wait Keywords ---
296
317
  /**
297
318
  * Waits for the specified element to be visible.
@@ -321,6 +342,22 @@ class Web {
321
342
  static async waitForElementNotVisible(to, timeout = 5000) {
322
343
  await this.getLocator(to).waitFor({ state: 'hidden', timeout });
323
344
  }
345
+ /**
346
+ * Waits for the page to load.
347
+ * @param timeout - The maximum time to wait in milliseconds (default: 30000).
348
+ * @param state - The load state to wait for ('load' | 'domcontentloaded' | 'networkidle'). Default is 'load'.
349
+ */
350
+ static async waitForPageLoad(timeout = 30000, state = 'load') {
351
+ await this.page.waitForLoadState(state, { timeout });
352
+ }
353
+ /**
354
+ * Waits for the next navigation to complete.
355
+ * @param timeout - The maximum time to wait in milliseconds (default: 30000).
356
+ * @param waitUntil - The event to wait for ('load' | 'domcontentloaded' | 'networkidle' | 'commit'). Default is 'load'.
357
+ */
358
+ static async waitForNextPageLoaded(timeout = 30000, waitUntil = 'load') {
359
+ await this.page.waitForNavigation({ timeout, waitUntil });
360
+ }
324
361
  /**
325
362
  * Delays execution for a specified number of seconds.
326
363
  * @param seconds - The number of seconds to wait.
@@ -387,6 +424,22 @@ class Web {
387
424
  }
388
425
  });
389
426
  }
427
+ /**
428
+ * Switches to the window/tab with the specified title.
429
+ * @param title - The title of the window to switch to.
430
+ */
431
+ static async switchToTabTitle(title) {
432
+ const pages = this.page.context().pages();
433
+ for (const p of pages) {
434
+ const pageTitle = await p.title();
435
+ if (pageTitle === title || pageTitle.includes(title)) {
436
+ await p.bringToFront();
437
+ Keyword_1.KeywordContext.page = p;
438
+ return;
439
+ }
440
+ }
441
+ throw new Error(`Tab with title '${title}' not found`);
442
+ }
390
443
  }
391
444
  exports.Web = Web;
392
445
  __decorate([
@@ -413,6 +466,12 @@ __decorate([
413
466
  __metadata("design:paramtypes", [ObjectRepository_1.TestObject, String, Object]),
414
467
  __metadata("design:returntype", Promise)
415
468
  ], Web, "setText", null);
469
+ __decorate([
470
+ (0, Keyword_1.Keyword)("Set Password"),
471
+ __metadata("design:type", Function),
472
+ __metadata("design:paramtypes", [ObjectRepository_1.TestObject, String, Object]),
473
+ __metadata("design:returntype", Promise)
474
+ ], Web, "setPassword", null);
416
475
  __decorate([
417
476
  (0, Keyword_1.Keyword)("Search"),
418
477
  __metadata("design:type", Function),
@@ -539,6 +598,12 @@ __decorate([
539
598
  __metadata("design:paramtypes", [ObjectRepository_1.TestObject, Boolean]),
540
599
  __metadata("design:returntype", Promise)
541
600
  ], Web, "verifyElementChecked", null);
601
+ __decorate([
602
+ (0, Keyword_1.Keyword)("Verify Element Clickable"),
603
+ __metadata("design:type", Function),
604
+ __metadata("design:paramtypes", [ObjectRepository_1.TestObject, Number]),
605
+ __metadata("design:returntype", Promise)
606
+ ], Web, "verifyElementClickable", null);
542
607
  __decorate([
543
608
  (0, Keyword_1.Keyword)("Wait For Element Visible"),
544
609
  __metadata("design:type", Function),
@@ -557,6 +622,18 @@ __decorate([
557
622
  __metadata("design:paramtypes", [ObjectRepository_1.TestObject, Number]),
558
623
  __metadata("design:returntype", Promise)
559
624
  ], Web, "waitForElementNotVisible", null);
625
+ __decorate([
626
+ (0, Keyword_1.Keyword)("Wait For Page Load"),
627
+ __metadata("design:type", Function),
628
+ __metadata("design:paramtypes", [Number, String]),
629
+ __metadata("design:returntype", Promise)
630
+ ], Web, "waitForPageLoad", null);
631
+ __decorate([
632
+ (0, Keyword_1.Keyword)("Wait For Next Page Loaded"),
633
+ __metadata("design:type", Function),
634
+ __metadata("design:paramtypes", [Number, String]),
635
+ __metadata("design:returntype", Promise)
636
+ ], Web, "waitForNextPageLoaded", null);
560
637
  __decorate([
561
638
  (0, Keyword_1.Keyword)("Delay"),
562
639
  __metadata("design:type", Function),
@@ -605,3 +682,9 @@ __decorate([
605
682
  __metadata("design:paramtypes", []),
606
683
  __metadata("design:returntype", Promise)
607
684
  ], Web, "waitForAngularLoad", null);
685
+ __decorate([
686
+ (0, Keyword_1.Keyword)("Switch To Tab Title"),
687
+ __metadata("design:type", Function),
688
+ __metadata("design:paramtypes", [String]),
689
+ __metadata("design:returntype", Promise)
690
+ ], Web, "switchToTabTitle", null);
@@ -86,6 +86,31 @@ class HtmlReporter {
86
86
  }
87
87
  fs.writeFileSync(this.reportPath, html);
88
88
  console.log(`HTML Report generated at: ${this.reportPath}`);
89
+ // Check if we should open the report
90
+ let openReport = true; // Default to true
91
+ try {
92
+ const configPath = path.join(process.cwd(), 'report.config.json');
93
+ if (fs.existsSync(configPath)) {
94
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
95
+ if (config.openReport === false) {
96
+ openReport = false;
97
+ }
98
+ }
99
+ }
100
+ catch (e) { /* ignore */ }
101
+ if (openReport) {
102
+ try {
103
+ // Write a marker file for VS Code extension to detect and open Latest Run view
104
+ const markerPath = path.join(process.cwd(), '.flash-report-ready');
105
+ fs.writeFileSync(markerPath, JSON.stringify({
106
+ reportPath: this.reportPath,
107
+ timestamp: Date.now()
108
+ }));
109
+ }
110
+ catch (e) {
111
+ console.log('Could not write report marker file.');
112
+ }
113
+ }
89
114
  }
90
115
  onTestEnd(test, result) {
91
116
  let suite = test.parent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flash-ai-team/flash-test-framework",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "A powerful keyword-driven automation framework built on top of Playwright and TypeScript.",
5
5
  "keywords": [
6
6
  "playwright",