├── . npmignore ├── .gitattributes ├── .github └── workflows │ ├── publish-release.yml │ └── run-tests.yml ├── .gitignore ├── .husky └── pre-commit ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── eslint.config.js ├── package-lock.json ├── package.json ├── playwright.config.ts ├── src └── vasu-playwright-lib │ ├── constants │ ├── index.ts │ ├── loadstate.ts │ └── timeouts.ts │ ├── index.ts │ ├── setup │ ├── custom-logger.ts │ └── index.ts │ ├── types │ ├── index.ts │ └── optional-parameter-types.ts │ └── utils │ ├── action-utils.ts │ ├── api-utils.ts │ ├── assert-utils.ts │ ├── element-utils.ts │ ├── index.ts │ ├── locator-utils.ts │ └── page-utils.ts ├── test-setup ├── global-setup.ts ├── global-teardown.ts └── page-setup.ts ├── tests ├── pages │ ├── sauce-demo │ │ ├── sauce-demo-login-page.ts │ │ ├── sauce-demo-mini-cart.ts │ │ └── sauce-demo-products-page.ts │ └── storage-setup │ │ └── login-storage.setup.ts ├── specs │ ├── sauce-demo-all-pass.spec.ts │ ├── sauce-demo-failure.spec.ts │ └── sauce-demo-parameterised.spec.ts └── testdata │ └── sauce-demo-test-data.ts ├── tsconfig.build.json └── tsconfig.json /. npmignore: -------------------------------------------------------------------------------- 1 | tests 2 | test-setup 3 | playwright.config.ts 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish package to NPM Packages 2 | on: 3 | release: 4 | types: [published] 5 | workflow_dispatch: 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | contents: read 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v4 14 | - name: Setup Node.js environment 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version: '22' 18 | registry-url: 'https://registry.npmjs.org/' 19 | 20 | - name: Install Libraries (ignore scripts) 21 | run: npm ci --ignore-scripts # This prevents postinstall and other scripts from running 22 | - name: Publish to NPM 23 | run: npm publish 24 | env: 25 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | test: 12 | name: PR Check 13 | runs-on: 14 | labels: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Setup node 20 | uses: actions/setup-node@v4 21 | with: 22 | # renovate: node-version 23 | node-version: '22' 24 | 25 | - name: Install Playwright Deps 26 | run: npx playwright install-deps chromium 27 | 28 | - name: Install Libraries 29 | run: npm ci 30 | 31 | - name: Run tests 32 | run: npm run test:chromium 33 | 34 | - name: Upload test results 35 | if: always() 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: playwright-test-results 39 | path: playwright-report 40 | # retention-days: 1 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | test-results/ 3 | playwright-report/ 4 | playwright/.cache/ 5 | playwright/.auth/ 6 | .env 7 | .vscode 8 | .idea 9 | .DS_Store 10 | allure* 11 | dist 12 | .secrets 13 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | npx lint-staged 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | test-results/ 3 | playwright-report/ 4 | playwright/.cache/ 5 | playwright/.auth/ 6 | .husky/ 7 | *.env 8 | .vscode 9 | .idea 10 | .DS_Store 11 | allure* 12 | dist 13 | 14 | package-lock.json 15 | **/*.sh 16 | **/*.png 17 | .prettierignore 18 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "overrides": [ 5 | { 6 | "files": ["*.json", ".eslintrc"], 7 | "options": { 8 | "trailingComma": "none" 9 | } 10 | } 11 | ], 12 | "singleQuote": true, 13 | "quoteProps": "as-needed", 14 | "printWidth": 120, 15 | "tabWidth": 2, 16 | "bracketSpacing": true, 17 | "arrowParens": "avoid", 18 | "endOfLine": "auto" 19 | } 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.20.0] - 2025-06-03 4 | 5 | ### Updated Dependencies 6 | 7 | - Updated `playwright` from version `1.48.1` to `1.52.0`. 8 | - Updated all corresponding dependencies to their latest versions. 9 | 10 | ### Updated 11 | 12 | - New ESLint v9 configuration 13 | - Added @eslint/js dependency 14 | - Removed .eslintrc.js and .eslintignore (ESLint v8 configuration) 15 | 16 | ## [1.19.0] - 2024-10-21 17 | 18 | ### Updated 19 | 20 | - Improved the `clickAndNavigate` method to handle element staleness and navigation errors more robustly. The method now waits for the element clicked to become hidden or stale after navigation and includes enhanced error handling for better debugging. 21 | 22 | ## [1.18.0] - 2024-10-17 23 | 24 | ### Updated Dependencies 25 | 26 | - Updating getAllLocators, getAllTexts, getLocatorCount to auto wait by adding waitForFirstElementToBeAttached method 27 | 28 | ## [1.17.0] - 2024-10-17 29 | 30 | ### Updated Dependencies 31 | 32 | - Updated `playwright` from version `1.44.1` to `1.48.1`. 33 | - Updated all corresponding dependencies to their latest versions. 34 | 35 | ### Added 36 | 37 | - New GitHub workflow to run tests on pushes to the main branch. 38 | - New GitHub workflow to publish the package to npm. 39 | 40 | ## [1.16.0] - 2024-09-19 41 | 42 | ### Updated Dependencies 43 | 44 | - Updated `playwright` from version `1.44.1` to `1.47.1`. 45 | - Updated all corresponding dependencies to their latest versions. 46 | 47 | ## [1.15.0] - 2024-05-24 48 | 49 | ### Updated Dependencies 50 | 51 | - Updated `playwright` from version `1.41.2` to `1.44.1`. 52 | - Updated all corresponding dependencies to their latest versions. 53 | 54 | ## [1.14.1] - 2024-03-25 55 | 56 | - Fix API utils module under exports in package.json 57 | 58 | ## [1.14.0] - 2024-03-18 59 | 60 | ### Added 61 | 62 | - API Utils for enhanced network interaction within tests. Includes utility functions for performing HTTP requests (GET, POST, PUT, DELETE) using Playwright's `APIRequestContext`. This addition simplifies the process of interacting with APIs directly from test scripts, providing a more integrated and efficient testing workflow. 63 | - `getAPIRequestContext()`: Retrieves the `APIRequestContext` from the current page, equivalent to the request object from Playwright's fixture, enabling seamless network operations within tests. 64 | - `getRequest(url: string, options?)`: Performs a GET request to the specified URL with optional configuration. 65 | - `postRequest(url: string, options?)`: Allows sending POST requests to a specified URL, supporting optional parameters for body content and headers. 66 | - `putRequest(url: string, options?)`: Facilitates PUT requests for updating resources at the specified URL, with support for optional configuration. 67 | - `deleteRequest(url: string, options?)`: Implements DELETE requests to remove resources from the specified URL, also supporting optional parameters. 68 | 69 | ## [0.13.0] - 2024-03-18 70 | 71 | ### Added 72 | 73 | - Stable optional parameter for `action-util` functions like `click`, `fill`, `check`, etc., to increase the reliability of actions performed in tests. 74 | - Enhanced `getFrame` functionality to throw an error if the frame is not found when the optional `force` parameter is set to `true`. Defaults to `false`. 75 | 76 | ### Changed 77 | 78 | - Moved `@playwright/test` dependency to `peerDependencies` to provide more flexibility in version management for users of the library. The required version is now `>=1.41.1`. 79 | - Updated all packages to their latest versions to incorporate the latest features and security updates. 80 | 81 | ### Replaced 82 | 83 | - `.eslintrc` configuration file with `.eslintrc.js` to allow for more dynamic and programmable ESLint configurations. 84 | 85 | ## [0.12.2] - 2024-02-21 86 | 87 | ### Fixed 88 | 89 | - Improved the `closePage` method for enhanced reliability and usability. The method now automatically invokes `switchToDefaultPage` when such a page is available. 90 | 91 | ## [0.12.1] - 2024-02-19 92 | 93 | ### Updated Dependencies 94 | 95 | - Updated all corresponding dependencies to their latest versions. 96 | 97 | ## [0.12.0] - 2024-02-19 98 | 99 | ### Added 100 | 101 | - New method `clearByJS` in case Playwright's clear function doesn't work as expected. This method provides an alternative way to clear input fields using JavaScript. 102 | - `clickAndNavigate` function now throws a more descriptive error message to facilitate easier debugging. 103 | - Added new method `fillAndTab` in `action-utils` for filling input fields and simulating a tab press, improving form handling automation. 104 | - New method `getContext` added in `page-utils`, offering a straightforward way to retrieve the browser context for advanced testing scenarios. 105 | 106 | ### Changed 107 | 108 | - `pressKeyboard` function is now divided into `pressPageKeyboard` and `pressLocatorKeyboard` functions to provide more targeted keyboard interaction capabilities. This change allows for more precise control over where keyboard inputs are sent, whether to the page or specific element locators. 109 | 110 | ### Breaking Changes 111 | 112 | - Splitting `pressKeyboard` into `pressPageKeyboard` and `pressLocatorKeyboard` may require updates to existing tests that utilize the original `pressKeyboard` method. This modification aims to enhance test script specificity and efficiency but will necessitate changes to maintain compatibility. 113 | 114 | ## [0.11.4] - 2024-02-01 115 | 116 | ### Updated Dependencies 117 | 118 | - Updated `playwright` from version `1.40.1` to `1.41.2`. 119 | - Updated all corresponding dependencies to their latest versions. 120 | 121 | ### Enhancements 122 | 123 | - **`acceptAlert`, `dismissAlert`, `getAlertText` Function**: Improved the functions to efficiently handle the alert dialogs. It now ensures that the function does not hang if an alert is not triggered after the specified timeout period. 124 | 125 | ### Added 126 | 127 | - **`expectAlertToHaveText` Function**: Introduced a new function for asserting that the text from an alert dialog exactly matches a provided string. 128 | 129 | - **`expectAlertToMatchText` Function**: Added a new function that allows assertions against the text from an alert dialog to be made using either a string for partial matches or a regular expression for pattern matching. 130 | 131 | ## [0.11.3] - 2024-01-31 132 | 133 | ### Added 134 | 135 | - Published the `src` folder in the npm package to facilitate easier debugging by consumers of the library. 136 | 137 | ## [0.11.2] - 2024-01-31 138 | 139 | ### Updated Dependencies 140 | 141 | - Updated `playwright` from version `1.40.0` to `1.41.1`. 142 | - Updated all corresponding dependencies to their latest versions. 143 | 144 | ## [0.11.1] - 2024-01-31 145 | 146 | ### Fixed 147 | 148 | - Corrected the path of the compiled `dist` folder by adjusting the `exports` section in `package.json` to properly include the `src` folder. This ensures correct resolution of module paths when the package is consumed. 149 | 150 | ## [0.11.0] - 2024-01-31 151 | 152 | ### Added 153 | 154 | - Added Tests within the repository to ensure library updates function as expected. 155 | 156 | ### Changed 157 | 158 | - Configuration to publish only the `src` folder, excluding test-related files and directories from the npm package. 159 | 160 | ## [0.10.1] - 2024-01-18 161 | 162 | ### Changed 163 | 164 | - Moved page related functions from `action-utils` and `element-utils` to `page-utils`. 165 | - `action-utils` to `page-utils` : `gotoURL`, `waitForPageLoadState`, `reloadPage`, `goBack`, `wait` 166 | - - `element-utils` to `page-utils` : `getURL`, `getWindowSize`, `saveStorageState` 167 | 168 | ## [0.10.0] - 2024-01-17 169 | 170 | ### Updated Dependencies 171 | 172 | - Updated `playwright` from version `1.40.1` to `1.41.0`. 173 | - Updated all corresponding dependencies to their latest versions. 174 | 175 | ## [0.9.0] - 2024-01-09 176 | 177 | ### Added 178 | 179 | - `expectElementNotToBeInViewport` reusable method for asserting an element's absence in the current viewport. 180 | - `onlyVisible` parameter to locators in action-utils for enhanced stability and precision. `setDefaultLocatorFilterVisibility` to allow dynamic configuration. 181 | - Getter and setter functions for `defaultLoadState` to allow dynamic configuration. 182 | 183 | ## [0.8.1] - 2023-12-14 184 | 185 | ### Updated 186 | 187 | - `getFrame` method now throws an error if the frame is not found. Added an optional `force` parameter for conditional error handling. 188 | - Updated all corresponding dependencies to their latest versions. 189 | 190 | ## [0.8.0] - 2023-12-03 191 | 192 | ### Changed 193 | 194 | - Moved `playwright` from peer dependencies to dependencies section and updated from version `1.38.1` to `1.40.1`. 195 | - Updated all corresponding dependencies to their latest versions. 196 | 197 | ### Updated Dependencies 198 | 199 | - `@types/node` from `^20.8.7` to `^20.10.3` 200 | - `@typescript-eslint/eslint-plugin` from `^6.8.0` to `^6.13.1` 201 | - `@typescript-eslint/parser` from `^6.8.0` to `^6.13.1` 202 | - `axios` from `^1.5.1` to `^1.6.2` 203 | - `eslint` from `^8.52.0` to `^8.55.0` 204 | - `eslint-config-prettier` from `^9.0.0` to `^9.1.0` 205 | - `eslint-plugin-jsdoc` from `^46.8.2` to `^46.9.0` 206 | - `eslint-plugin-playwright` from `^0.18.0` to `^0.19.0` 207 | - `lint-staged` from `^15.0.2` to `^15.2.0` 208 | - `prettier` from `^3.0.3` to `^3.1.0` 209 | - `typescript` from `5.2.2` to `5.3.2` 210 | 211 | ### Notes 212 | 213 | - This update includes significant dependency upgrades and a change in the way Playwright is included in the project. It is recommended to test existing functionalities thoroughly to ensure compatibility with the new versions. 214 | 215 | ## [0.7.3] - 2023-12-03 216 | 217 | ### Added 218 | 219 | - Added `pressKeyboard` method to simulate keyboard interactions, such as pressing keys or key combinations. 220 | - Added `getWindowSize` method to retrieve the current size of the browser window, including width and height. 221 | 222 | ## [0.7.2] - 2023-10-24 223 | 224 | ### Updated 225 | 226 | - Updated package versions. Major updates to current latest versions include `@typescript-eslint/eslint-plugin`, `@typescript-eslint/parser`, `allure-playwright`, and `lint-staged`. 227 | 228 | ## [0.7.1] - 2023-10-23 229 | 230 | ### Updated 231 | 232 | - Changed the version specifier for the `@playwright/test` peer dependency from `^1.38.1` to `~1.38.1` to pin to patch releases and avoid a known issue in version 1.39.0. For more details on the issue, see [microsoft/playwright#27733](https://github.com/microsoft/playwright/issues/27733). 233 | 234 | ## [0.7.0] - 2023-09-25 235 | 236 | ### Breaking Changes 237 | 238 | - Replaced the `type` reusable function with `pressSequentially`. This change enhances the typing simulation but may require updates to existing test scripts that use the `type` function. 239 | 240 | ### Updated 241 | 242 | - Upgraded the Playwright dependency from version 1.37.1 to 1.38.1 to leverage new features and performance improvements. 243 | 244 | ## [0.6.3] - 2023-09-25 245 | 246 | ### Updated 247 | 248 | - Upgraded all dependencies to their latest stable versions. 249 | 250 | ## [0.6.2] - 2023-09-25 251 | 252 | ### Fixed 253 | 254 | - **`closePage` Function**: Resolved an issue where the optional parameter was not being handled correctly. This fix enhances the function's reliability and prevents unexpected behavior. 255 | 256 | ## [0.6.1] - 2023-09-19 257 | 258 | ### Fixed 259 | 260 | - Fixed the assertions for page size. 261 | 262 | ## [0.6.0] - 2023-09-19 263 | 264 | ### Added 265 | 266 | - Added `getAllPages` and `expectPageSizeToBeEqualTo` reusable functions. 267 | - Added `getFrame` reusable function. 268 | 269 | ### Changed 270 | 271 | - `saveStorageState` function will now return the storage state instead of `void`. 272 | 273 | ### Updated 274 | 275 | - Updated all the dependencies to the latest versions. 276 | 277 | ### Optimized 278 | 279 | - Optimized `page-utils` with better error handling for `switchPage`, `switchToDefaultPage`, `closePage` functions. 280 | - Added `SwitchPageOptions` optional parameter type to `switchPage`. 281 | 282 | ## [0.5.4] - 2023-09-03 283 | 284 | ### Added 285 | 286 | - Added new utility functions in `element-util`: 287 | - `waitForElementToBeAttached`: Waits for an element to be attached to the DOM. 288 | - `waitForElementToBeDetached`: Waits for an element to be detached from the DOM. 289 | - `waitForElementToBeVisible`: Waits for an element to be visible on the page. 290 | - `waitForElementToBeHidden`: Waits for an element to be hidden on the page. 291 | 292 | ## [0.5.3] - 2023-09-03 293 | 294 | ### Added 295 | 296 | - Introduced `waitForElementToBeStable` function to improve element stability checks during test execution. This function waits for an element to be stable in its position before proceeding with further actions. 297 | 298 | ## [0.5.2] - 2023-09-01 299 | 300 | ### Fixed 301 | 302 | - Fixed `clickByJS` to work correctly with both `SVGElement` and `HTMLElement` by adding type checks. 303 | 304 | ## [0.5.1] - 2023-08-30 305 | 306 | ### Added 307 | 308 | - Introduced axios and Allure report as new dependencies. 309 | 310 | ### Updated 311 | 312 | - Upgraded all dependencies to their latest stable versions. 313 | 314 | ## [0.5.0] - 2023-08-27 315 | 316 | ### Added 317 | 318 | - Added Prettier linting support for Windows via the `cross-env` package. 319 | - Included `src` directory in the npm package for easier source code debugging. 320 | 321 | ## [0.4.6] - 2023-08-23 322 | 323 | ### Fixed 324 | 325 | - Removed the `postinstall` script to fix the installation issues. 326 | 327 | ## [0.4.5] - 2023-08-24 328 | 329 | ### Added 330 | 331 | - Extended API by exporting various utility classes like `ActionUtils`, `AssertUtils`, `ElementUtils`, `LocatorUtils`, `PageUtils`, `ParameterTypes`, and `Timeouts`. 332 | 333 | ## [0.4.4] - 2023-08-22 334 | 335 | ### Added 336 | 337 | - Exported `custom-logger` for enhanced logging capabilities. 338 | 339 | ## [0.4.3] - 2023-08-22 340 | 341 | ### Changed 342 | 343 | - Removed `page-setup` library to avoid conflicts with Playwright Test's `test.beforeEach()` method. 344 | 345 | ## [0.4.2] - 2023-08-22 346 | 347 | ### Fixed 348 | 349 | - Addressed an issue preventing the import of `Customlogger`. 350 | 351 | ## [0.4.1] - 2023-08-22 352 | 353 | ### Fixed 354 | 355 | - Resolved import issues related to newly added libraries. 356 | 357 | ## [0.4.0] - 2023-08-22 358 | 359 | ### Changed 360 | 361 | - Optimized `devDependencies`, `dependencies`, and `peerDependencies` for better project compatibility. 362 | 363 | ### Updated 364 | 365 | - Updated all dependencies to the latest versions. 366 | 367 | ### Added 368 | 369 | - Introduced `assert-utils` for advanced Playwright assertions. 370 | - Added `custom-logger` for improved and colored console output. 371 | - Implemented `page-setup` for pre-test page configurations. 372 | 373 | ## [0.3.0] - 2023-08-22 374 | 375 | ### Changed 376 | 377 | - Migrated from `playwright` to `@playwright/test` for enhanced test handling. 378 | 379 | ## [0.2.0] - 2023-08-21 380 | 381 | ### Added 382 | 383 | - Enabled relative imports for utility functions. 384 | 385 | ## [0.1.0] - 2023-08-20 386 | 387 | ### Added 388 | 389 | - Initial release with basic functionalities. 390 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2023] [Vasudeva Chowdary Annam] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Playwright TypeScript Utility Functions Library 2 | 3 | [![GitHub stars](https://img.shields.io/github/stars/vasu31dev/playwright-ts-lib)](https://github.com/vasu31dev/playwright-ts-lib/stargazers) 4 | ![Last Commit](https://img.shields.io/github/last-commit/vasu31dev/playwright-ts-lib) ![Pull Requests](https://img.shields.io/github/issues-pr-raw/vasu31dev/playwright-ts-lib) 5 | [![npm version](https://img.shields.io/npm/v/vasu-playwright-utils.svg)](https://www.npmjs.com/package/vasu-playwright-utils) 6 | [![Release Notes](https://img.shields.io/badge/Release%20Notes-📝-brightgreen)](https://github.com/vasu31dev/playwright-ts-lib/blob/main/CHANGELOG.md) 7 | 8 | ## About 9 | 10 | `playwright-ts-lib` is a TypeScript library that provides reusable helper methods for the Playwright Library. It aims to simplify common tasks and enhance the efficiency of automated testing workflows. 11 | 12 | ## Table of Contents 13 | 14 | - [Installation](#installation) 15 | - [Usage](#usage) 16 | - [License](#license) 17 | 18 | ## Installation 19 | 20 | To install the library, run the following command: 21 | 22 | ```bash 23 | npm install vasu-playwright-utils 24 | ``` 25 | 26 | ## Usage 27 | 28 | Here's a simple example of how you can use the library: 29 | 30 | ```typescript 31 | import { click, fill } from 'vasu-playwright-utils'; 32 | 33 | // Your code here 34 | ``` 35 | 36 | ## Issues and Feedback 37 | 38 | If you encounter any issues or have feedback, please [Raise an Issue](https://github.com/vasu31dev/playwright-ts-lib/issues) on GitHub. 39 | 40 | ## Contributions 41 | 42 | We welcome contributions! Feel free to submit a [Pull Request](https://github.com/vasu31dev/playwright-ts-lib/pulls) on GitHub. 43 | 44 | ## License 45 | 46 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 47 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | const typescript = require('@typescript-eslint/eslint-plugin'); 2 | const typescriptParser = require('@typescript-eslint/parser'); 3 | const prettier = require('eslint-plugin-prettier'); 4 | const importPlugin = require('eslint-plugin-import'); 5 | const jsdoc = require('eslint-plugin-jsdoc'); 6 | const playwright = require('eslint-plugin-playwright'); 7 | const js = require('@eslint/js'); 8 | 9 | module.exports = [ 10 | { ignores: ['node_modules/**', '.husky/**', '**/*.js', 'package-lock.json', 'dist/**/*'] }, 11 | js.configs.recommended, 12 | { 13 | files: ['**/*.ts'], 14 | languageOptions: { 15 | parser: typescriptParser, 16 | parserOptions: { project: './tsconfig.json', tsconfigRootDir: __dirname }, 17 | globals: { 18 | console: 'readonly', 19 | process: 'readonly', 20 | Buffer: 'readonly', 21 | __dirname: 'readonly', 22 | __filename: 'readonly', 23 | global: 'readonly', 24 | module: 'readonly', 25 | require: 'readonly', 26 | exports: 'readonly', 27 | }, 28 | }, 29 | plugins: { 30 | '@typescript-eslint': typescript, 31 | prettier: prettier, 32 | import: importPlugin, 33 | jsdoc: jsdoc, 34 | playwright: playwright, 35 | }, 36 | settings: { 'import/resolver': { typescript: { alwaysTryTypes: true } } }, 37 | rules: { 38 | // Prettier Rules 39 | 'prettier/prettier': 'error', 40 | 'no-trailing-spaces': 'error', 41 | 'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0 }], 42 | 43 | // TypeScript Rules 44 | ...typescript.configs['eslint-recommended'].overrides[0].rules, 45 | ...typescript.configs.recommended.rules, 46 | '@typescript-eslint/no-unsafe-assignment': 'warn', 47 | '@typescript-eslint/no-unsafe-member-access': 'warn', 48 | '@typescript-eslint/no-unsafe-return': 'warn', 49 | '@typescript-eslint/no-floating-promises': 'error', 50 | '@typescript-eslint/no-unnecessary-type-assertion': 'error', 51 | '@typescript-eslint/no-explicit-any': 'warn', 52 | '@typescript-eslint/explicit-module-boundary-types': 'off', 53 | 54 | // Import Rules 55 | 'import/no-unresolved': 'error', 56 | 'import/named': 'error', 57 | 'import/default': 'error', 58 | 'import/no-absolute-path': 'error', 59 | 'import/no-self-import': 'error', 60 | 'sort-imports': ['error', { ignoreDeclarationSort: true }], 61 | 62 | // General Rules 63 | 'require-await': 'error', 64 | complexity: ['warn', { max: 11 }], 65 | 66 | // JSDoc Rules 67 | 'jsdoc/check-alignment': 'warn', 68 | 'jsdoc/check-indentation': 'warn', 69 | 70 | // Playwright Rules 71 | 'playwright/missing-playwright-await': ['error'], 72 | 'playwright/no-focused-test': 'error', 73 | 'playwright/valid-expect': 'error', 74 | 'playwright/prefer-web-first-assertions': 'error', 75 | 'playwright/no-useless-await': 'error', 76 | 'playwright/no-page-pause': 'error', 77 | 'playwright/no-element-handle': 'error', 78 | 'playwright/no-eval': 'error', 79 | 'playwright/prefer-to-be': 'error', 80 | 'playwright/prefer-to-contain': 'error', 81 | 'playwright/prefer-to-have-length': 'error', 82 | 'playwright/no-wait-for-timeout': 'warn', 83 | 'playwright/no-useless-not': 'warn', 84 | 'playwright/require-top-level-describe': 'off', 85 | 'playwright/expect-expect': 'off', 86 | 'playwright/no-conditional-in-test': 'off', 87 | 'playwright/no-skipped-test': 'off', 88 | }, 89 | }, 90 | { files: ['src/**/*.ts'], rules: { '@typescript-eslint/explicit-module-boundary-types': 'warn' } }, 91 | ]; 92 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vasu-playwright-utils", 3 | "version": "1.20.0", 4 | "description": "Playwright Typescript Library with reusable utilities", 5 | "main": "./dist/src/vasu-playwright-lib/index.js", 6 | "types": "./dist/src/vasu-playwright-lib/index.d.ts", 7 | "exports": { 8 | ".": "./dist/src/vasu-playwright-lib/index.js", 9 | "./action-utils": "./dist/src/vasu-playwright-lib/utils/action-utils.js", 10 | "./assert-utils": "./dist/src/vasu-playwright-lib/utils/assert-utils.js", 11 | "./element-utils": "./dist/src/vasu-playwright-lib/utils/element-utils.js", 12 | "./locator-utils": "./dist/src/vasu-playwright-lib/utils/locator-utils.js", 13 | "./page-utils": "./dist/src/vasu-playwright-lib/utils/page-utils.js", 14 | "./api-utils": "./dist/src/vasu-playwright-lib/utils/api-utils.js", 15 | "./constants": "./dist/src/vasu-playwright-lib/constants/index.js", 16 | "./setup": "./dist/src/vasu-playwright-lib/setup/index.js", 17 | "./custom-logger": "./dist/src/vasu-playwright-lib/setup/custom-logger.js", 18 | "./types": "./dist/src/vasu-playwright-lib/types/optional-parameter-types.js" 19 | }, 20 | "keywords": [ 21 | "playwright", 22 | "typescript", 23 | "library" 24 | ], 25 | "license": "MIT", 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/vasu31dev/playwright-ts-lib.git" 29 | }, 30 | "author": { 31 | "name": "Vasudeva Annam", 32 | "email": "vasu31dev@gmail.com" 33 | }, 34 | "homepage": "https://github.com/vasu31dev/playwright-ts-lib#readme", 35 | "bugs": { 36 | "url": "https://github.com/vasu31dev/playwright-ts-lib/issues" 37 | }, 38 | "publishConfig": { 39 | "access": "public" 40 | }, 41 | "files": [ 42 | "dist", 43 | "src" 44 | ], 45 | "engines": { 46 | "node": ">=18.0.0" 47 | }, 48 | "dependencies": { 49 | "@eslint/js": "^9.28.0", 50 | "@types/node": "^22.15.29", 51 | "@typescript-eslint/eslint-plugin": "^8.33.1", 52 | "@typescript-eslint/parser": "^8.33.1", 53 | "allure-commandline": "^2.34.0", 54 | "allure-playwright": "^3.2.2", 55 | "axios": "^1.9.0", 56 | "cross-env": "^7.0.3", 57 | "dotenv": "^16.5.0", 58 | "eslint": "^9.28.0", 59 | "eslint-config-prettier": "^10.1.5", 60 | "eslint-import-resolver-typescript": "^4.4.2", 61 | "eslint-plugin-import": "^2.31.0", 62 | "eslint-plugin-jsdoc": "^50.7.1", 63 | "eslint-plugin-playwright": "^2.2.0", 64 | "eslint-plugin-prettier": "^5.4.1", 65 | "husky": "^9.1.7", 66 | "lint-staged": "^16.1.0", 67 | "prettier": "^3.5.3", 68 | "rimraf": "^6.0.1", 69 | "tslib": "^2.8.1", 70 | "typescript": "5.8.3", 71 | "winston": "^3.17.0" 72 | }, 73 | "peerDependencies": { 74 | "@playwright/test": ">=1.52.0" 75 | }, 76 | "scripts": { 77 | "clean": "rimraf dist", 78 | "prebuild": "npm run clean", 79 | "build": "tsc -p tsconfig.build.json", 80 | "validate": "tsc --noEmit", 81 | "prepublishOnly": "npm run build", 82 | "postpublish": "npm run clean", 83 | "ready": "rimraf dist node_modules package-lock.json && npm i && npm run validate && npm run build && npm run test", 84 | "test": "playwright test", 85 | "test:chromium": "playwright test --retries 0 --project=chromiumheadless", 86 | "test:chromium-headed": "playwright test -j 1 --retries 0 --headed --project=chromium", 87 | "local": "playwright test -j 1 --retries 0 --headed --project=chromium", 88 | "test:reg": "playwright test --project=chromiumheadless -g '@reg'", 89 | "test:smoke": "playwright test --project=chromiumheadless -g '@smoke'", 90 | "report": "playwright show-report playwright-report", 91 | "ui": "playwright test --project=chromiumheadless --ui-port 0", 92 | "trace": "playwright show-trace --port 0", 93 | "allure": "allure generate ./allure-results --clean && allure open ./allure-report", 94 | "link": "npm run build && npm link", 95 | "lint": "cross-env eslint 'src/**/*.ts' 'tests/**/*.ts' 'test-setup/**/*.ts' 'playwright.config.ts'", 96 | "lint:fix": "cross-env eslint 'src/**/*.ts' 'tests/**/*.ts' 'test-setup/**/*.ts' 'playwright.config.ts' --fix", 97 | "prepare": "husky", 98 | "format": "cross-env prettier --write 'src/**/*.ts' 'tests/**/*.ts' 'test-setup/**/*.ts' 'playwright.config.ts' '**/*.json' '**/*.md' '!package-lock.json' '!dist/**/*' '!build/**/*'", 99 | "postinstall": "playwright install chromium" 100 | }, 101 | "husky": { 102 | "hooks": { 103 | "pre-commit": "lint-staged" 104 | } 105 | }, 106 | "lint-staged": { 107 | "*.{json,md,ts}": [ 108 | "cross-env prettier --write" 109 | ], 110 | "*.{ts}": [ 111 | "cross-env eslint --fix" 112 | ] 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * playwright.config.ts: This module is responsible for configuring the Playwright test runner. 3 | * It includes settings for test execution, browser configuration, and environment variables. 4 | * See https://playwright.dev/docs/test-configuration for more details. 5 | */ 6 | 7 | import { ACTION_TIMEOUT, EXPECT_TIMEOUT, NAVIGATION_TIMEOUT, TEST_TIMEOUT } from '@constants/timeouts'; 8 | import { defineConfig, devices } from '@playwright/test'; 9 | import dotenv from 'dotenv'; 10 | dotenv.config({ path: '.env' }); 11 | import os from 'os'; 12 | 13 | const BASE_URL = process.env.URL || 'https://www.saucedemo.com/'; 14 | const startLocalHost = process.env.URL && process.env.URL.includes('localhost'); 15 | export const LOCAL_HOST_URL = 'https://localhost:9002'; // Update the URL to match your local dev server URL 16 | const customLoggerPath = require.resolve('src/vasu-playwright-lib/setup/custom-logger'); 17 | 18 | export default defineConfig({ 19 | /** 20 | * The directory where tests are located. 21 | * See https://playwright.dev/docs/api/class-testconfig#testconfig-testdir 22 | */ 23 | testDir: './tests', 24 | /** 25 | * Determines whether to run tests within each spec file in parallel, in addition to running the spec files themselves in parallel. 26 | * See https://playwright.dev/docs/api/class-testconfig#testconfig-fullyparallel 27 | */ 28 | fullyParallel: false, 29 | /** 30 | * Whether to fail the build on CI if you accidentally left test.only in the source code. 31 | * See https://playwright.dev/docs/api/class-testconfig#testconfig-forbidonly 32 | */ 33 | forbidOnly: !!process.env.CI, 34 | /** 35 | * The number of times to retry failed tests. Retries value is only set to happen on CI. 36 | * See https://playwright.dev/docs/api/class-testconfig#testconfig-retries 37 | */ 38 | retries: process.env.CI ? 2 : 0, 39 | /** 40 | * The number of worker threads to use for running tests. This is set to a different value on CI. 41 | * See https://playwright.dev/docs/api/class-testconfig#testconfig-workers 42 | */ 43 | workers: process.env.CI ? 3 : 6, 44 | /* Note: Add allure-playwright report */ 45 | /** 46 | * The reporter to use. This can be set to use a different value on CI. 47 | * See https://playwright.dev/docs/test-reporters 48 | */ 49 | reporter: [[customLoggerPath], ['html', { open: 'never' }], ['dot']], 50 | /** 51 | * Shared settings for all the projects below. 52 | * See https://playwright.dev/docs/api/class-testoptions 53 | */ 54 | globalSetup: require.resolve('./test-setup/global-setup'), 55 | globalTeardown: require.resolve('./test-setup/global-teardown'), 56 | timeout: TEST_TIMEOUT, 57 | expect: { 58 | timeout: EXPECT_TIMEOUT, 59 | }, 60 | use: { 61 | /* Sets extra headers for CloudFlare. */ 62 | extraHTTPHeaders: { 63 | 'CF-Access-Client-Id': process.env.CF_CLIENT_ID || '', 64 | 'CF-Access-Client-Secret': process.env.CF_CLIENT_SECRET || '', 65 | }, 66 | ignoreHTTPSErrors: true, 67 | acceptDownloads: true, 68 | testIdAttribute: 'data-testid', 69 | /** 70 | * The base URL to be used in navigation actions such as `await page.goto('/')`. 71 | * This allows for shorter and more readable navigation commands in the tests. 72 | */ 73 | baseURL: BASE_URL, 74 | /* Records traces after each test failure for debugging purposes. */ 75 | trace: 'retain-on-failure', 76 | /* Captures screenshots after each test failure to provide visual context. */ 77 | screenshot: 'only-on-failure', 78 | /* Sets a timeout for actions like click, fill, select to prevent long-running operations. */ 79 | actionTimeout: ACTION_TIMEOUT, 80 | /* Sets a timeout for page loading navigations like goto URL, go back, reload, waitForNavigation to prevent long page loads. */ 81 | navigationTimeout: NAVIGATION_TIMEOUT, 82 | }, 83 | 84 | /** 85 | * Configure projects for major browsers. 86 | * See https://playwright.dev/docs/test-configuration#projects 87 | */ 88 | projects: [ 89 | /** Due to different view ports in Head and Headless, created 2 projects one for head mode and the same browser for headless. */ 90 | { 91 | name: 'chromium', 92 | use: { 93 | viewport: null, 94 | launchOptions: { 95 | args: ['--disable-web-security', '--start-maximized'], 96 | // channel: 'chrome', 97 | slowMo: 0, 98 | headless: false, 99 | }, 100 | }, 101 | }, 102 | 103 | { 104 | name: 'chromiumheadless', 105 | use: { 106 | ...devices['Desktop Chrome'], 107 | viewport: { width: 1920, height: 1080 }, 108 | launchOptions: { 109 | args: ['--disable-web-security'], 110 | // channel: 'chrome', 111 | slowMo: 0, 112 | headless: true, 113 | }, 114 | }, 115 | }, 116 | 117 | /******* Uncomment to run tests in other browsers 118 | { 119 | name: 'firefox', 120 | use: { 121 | ...devices['Desktop Firefox'], 122 | viewport: { width: 1600, height: 1000 }, 123 | launchOptions: { 124 | firefoxUserPrefs: { 125 | 'browser.cache.disk.enable': false, 126 | 'browser.cache.memory.enable': false, 127 | }, 128 | }, 129 | }, 130 | }, 131 | 132 | { 133 | name: 'webkit', 134 | use: { 135 | ...devices['Desktop Safari'], 136 | viewport: { width: 1600, height: 1000 }, 137 | }, 138 | }, 139 | 140 | // Test against mobile viewports. 141 | { 142 | name: 'Mobile Chrome', 143 | use: { ...devices['Pixel 5'] }, 144 | }, 145 | { 146 | name: 'Mobile Safari', 147 | use: { ...devices['iPhone 12'] }, 148 | }, 149 | 150 | // Test against branded browsers. 151 | { 152 | name: 'Microsoft Edge', 153 | use: { ...devices['Desktop Edge'], channel: 'msedge' }, 154 | }, 155 | { 156 | name: 'Google Chrome', 157 | use: { ...devices['Desktop Chrome'], channel: 'chrome' }, 158 | }, 159 | 160 | ***************/ 161 | ], 162 | 163 | /** 164 | * If the tests are being run on localhost, this configuration starts a web server. 165 | * See https://playwright.dev/docs/test-webserver#configuring-a-web-server 166 | */ 167 | ...(startLocalHost && { 168 | webServer: { 169 | cwd: `${os.homedir()}/repos/ui`, // You can also use the realtive path to the UI repo 170 | command: 'npm start ui-server', // Start the UI server 171 | url: LOCAL_HOST_URL, 172 | ignoreHTTPSErrors: true, 173 | timeout: 60 * 1000, 174 | reuseExistingServer: true, 175 | stdout: 'pipe', 176 | stderr: 'pipe', 177 | }, 178 | }), 179 | }); 180 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loadstate'; 2 | export * from './timeouts'; 3 | export * as Timeouts from './timeouts'; 4 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/constants/loadstate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * loadstate.ts 3 | * 4 | * This module provides two key constants: `defaultLoadState` and `defaultVisibleOnlyOption`. 5 | * 6 | * - `defaultLoadState` is used to set the initial page load state. It serves as the default value 7 | * in functions such as `gotoURL`, `waitForPageLoadState`, and `clickAndNavigate`. 8 | * 9 | * - `defaultVisibleOnlyOption` configures the default visibility setting for locators. It dictates 10 | * whether locators should, by default, find only visible elements. This setting is particularly 11 | * useful in action utilities like `click`, `fill` etc. 12 | * 13 | * Both constants can be initialized within this module and can be overridden in specific functions 14 | * as necessary, allowing for flexible and context-specific configurations. 15 | * 16 | * @module loadstate 17 | */ 18 | 19 | import { WaitForLoadStateOptions } from '../types/optional-parameter-types'; 20 | 21 | /** 22 | * Represents the load state option for waiting for specific events during page navigation. 23 | * This constant is utilized in the gotoURL, waitForPageLoadState, and clickAndNavigate helper functions. 24 | * It can be set to values like 'load', 'domcontentloaded', or 'networkidle'. 25 | */ 26 | let defaultLoadState: WaitForLoadStateOptions = 'domcontentloaded'; 27 | 28 | export function getDefaultLoadState(): WaitForLoadStateOptions { 29 | return defaultLoadState; 30 | } 31 | 32 | export function setDefaultLoadState(value: WaitForLoadStateOptions): void { 33 | defaultLoadState = value; 34 | } 35 | 36 | /** 37 | * Default visibility setting for locators. 38 | * This object holds the global configuration for whether locators should by default only find visible elements. 39 | * It can be used across the project to maintain a consistent behavior for element visibility handling. 40 | */ 41 | export const defaultVisibleOnlyOption = { onlyVisible: true }; 42 | 43 | /** 44 | * Sets the default visibility filter for locator functions. 45 | * This function allows changing the global default setting for whether locators should only find visible elements. 46 | * @param value - The value to set for the default visibility filter (true if only visible elements should be found). 47 | */ 48 | export function setDefaultLocatorFilterVisibility(value: boolean): void { 49 | defaultVisibleOnlyOption.onlyVisible = value; 50 | } 51 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/constants/timeouts.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * timeouts.ts: This module provides Timeout constants that can be used to override various actions, conditional statements and assertions. 3 | * Instead of hard coding the timeout when overriding any utility functions, use these Timeout constants. 4 | */ 5 | 6 | /** 7 | * Timeout constant for instant actions/assertions, set to 1000 milliseconds (1 second). 8 | */ 9 | export const INSTANT_TIMEOUT = 1000; 10 | 11 | /** 12 | * Timeout constant for small actions/assertions, set to 5000 milliseconds (5 seconds). 13 | */ 14 | export const SMALL_TIMEOUT = 5 * 1000; 15 | 16 | /** 17 | * Standard timeout constant, set to 15000 milliseconds (15 seconds). 18 | */ 19 | export const STANDARD_TIMEOUT = 15 * 1000; 20 | 21 | /** 22 | * Timeout constant for bigger actions/assertions, set to 30000 milliseconds (30 seconds). 23 | */ 24 | export const BIG_TIMEOUT = 30 * 1000; 25 | 26 | /** 27 | * Maximum timeout constant, set to 60000 milliseconds (1 minute). 28 | */ 29 | export const MAX_TIMEOUT = 60 * 1000; 30 | 31 | /** 32 | * Timeout constants used in the playwright.config.ts file. 33 | */ 34 | 35 | /** 36 | * Timeout constant for Playwright's expect function, set to 5000 milliseconds (5 seconds). 37 | */ 38 | export const EXPECT_TIMEOUT = 5 * 1000; 39 | 40 | /** 41 | * Timeout constant for Playwright's action functions, set to 5000 milliseconds (5 seconds). 42 | */ 43 | export const ACTION_TIMEOUT = 5 * 1000; 44 | 45 | /** 46 | * Timeout constant for Playwright's navigation functions, set to 30000 milliseconds (30 seconds). 47 | */ 48 | export const NAVIGATION_TIMEOUT = 30 * 1000; 49 | 50 | /** 51 | * Timeout constant for Playwright's test functions, set to 120000 milliseconds (2 minutes). 52 | */ 53 | export const TEST_TIMEOUT = 2 * 60 * 1000; 54 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils'; 2 | export * from './constants'; 3 | export * from './setup'; 4 | export * from './types'; 5 | export { default as CustomLogger } from './setup/custom-logger'; 6 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/setup/custom-logger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * custom-logger.ts: This module provides a custom logger for Playwright tests. It implements the Reporter interface from Playwright 3 | * and uses the Winston logging library to provide detailed logs for test execution. The logger includes custom colors 4 | * for different log levels and can be configured to log to the console or a file. 5 | */ 6 | 7 | import { Reporter, TestCase, TestError, TestResult } from '@playwright/test/reporter'; 8 | import winston from 'winston'; 9 | 10 | /** 11 | * Custom colors for the logger 12 | */ 13 | const customColors = { 14 | info: 'blue', 15 | error: 'red', 16 | }; 17 | winston.addColors(customColors); 18 | 19 | /** 20 | * Logger configuration 21 | */ 22 | export const logger = winston.createLogger({ 23 | level: 'info', 24 | format: winston.format.combine( 25 | winston.format.colorize({ all: true }), 26 | winston.format.timestamp(), 27 | winston.format.printf(({ timestamp, level, message }) => { 28 | return `${timestamp} [${level}]: ${message}`; 29 | }), 30 | ), 31 | transports: [ 32 | new winston.transports.Console(), 33 | // If you want to log to a file uncomment below line 34 | // new winston.transports.File({ filename: 'logs/info.log', level: 'info' }), 35 | ], 36 | }); 37 | 38 | /** 39 | * CustomLogger class that implements the Reporter interface from Playwright 40 | */ 41 | export default class CustomLogger implements Reporter { 42 | /** 43 | * Logs the start of a test case 44 | * @param {TestCase} test - The test case that is starting 45 | */ 46 | onTestBegin(test: TestCase): void { 47 | logger.info(`Test Case Started : ${test.title}`); 48 | } 49 | 50 | /** 51 | * Logs the end of a test case 52 | * @param {TestCase} test - The test case that ended 53 | * @param {TestResult} result - The result of the test case 54 | */ 55 | onTestEnd(test: TestCase, result: TestResult): void { 56 | if (result.status === 'passed') { 57 | logger.info(`\x1b[32mTest Case Passed : ${test.title}\x1b[0m`); // Green color 58 | } else if (result.status === 'skipped') { 59 | logger.info(`\x1b[33mTest Case Skipped : ${test.title}\x1b[0m`); // Yellow color 60 | } else if (result.status === 'failed' && result.error) { 61 | // Playwright inbuild reporter logs the error 62 | // logger.error( 63 | // `Test Case Failed: ${test.title} Error: ${result.error.message}`, 64 | // ); 65 | } 66 | } 67 | 68 | /** 69 | * Logs an error 70 | * @param {TestError} error - The error 71 | */ 72 | onError(error: TestError): void { 73 | logger.error(error.message); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/setup/index.ts: -------------------------------------------------------------------------------- 1 | export * from './custom-logger'; 2 | export * as CustomLogger from './custom-logger'; 3 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/types/index.ts: -------------------------------------------------------------------------------- 1 | // export * from './optional-parameter-types'; 2 | export * as ParameterTypes from './optional-parameter-types'; 3 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/types/optional-parameter-types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Types.ts: This module provides type definitions that are used as optional parameters for utility functions in other modules. 3 | * These types are based on the parameters of Playwright's built-in methods and are used to provide type safety and code completion. 4 | */ 5 | 6 | import { Locator, Page } from '@playwright/test'; 7 | 8 | /** 9 | * 1. Navigation Options: These types are used for navigation actions such as going to a URL, reloading a page, or waiting for a certain load state. 10 | * They are based on the parameters of Playwright's built-in navigation methods. 11 | */ 12 | export type GotoOptions = Parameters[1]; 13 | export type NavigationOptions = Parameters[0]; // Same for GoBack, GoForward 14 | export type WaitForLoadStateOptions = Parameters[0]; 15 | 16 | /** 17 | * 2. Action Options: These types are used for actions such as clicking, filling input fields, typing, etc. 18 | * They are based on the parameters of Playwright's built-in action methods. 19 | */ 20 | export type VisibilityOption = { onlyVisible?: boolean }; 21 | export type StabilityOption = { stable?: boolean }; 22 | export type LoadstateOption = { loadState?: WaitForLoadStateOptions }; 23 | export type ClickOptions = Parameters[0] & VisibilityOption & StabilityOption & LoadstateOption; 24 | export type FillOptions = Parameters[1] & VisibilityOption & StabilityOption; 25 | export type PressSequentiallyOptions = Parameters[1] & VisibilityOption & StabilityOption; 26 | export type ClearOptions = Parameters[0] & VisibilityOption & StabilityOption; 27 | export type SelectOptions = Parameters[1] & VisibilityOption & StabilityOption; 28 | export type CheckOptions = Parameters[0] & VisibilityOption & StabilityOption; 29 | export type HoverOptions = Parameters[0] & VisibilityOption & StabilityOption; 30 | export type UploadOptions = Parameters[1] & VisibilityOption & StabilityOption; 31 | export type UploadValues = Parameters[0] & VisibilityOption; 32 | export type DragOptions = Parameters[1] & VisibilityOption & StabilityOption; 33 | export type DoubleClickOptions = Parameters[0] & VisibilityOption & StabilityOption; 34 | export type ActionOptions = 35 | | ClickOptions 36 | | FillOptions 37 | | PressSequentiallyOptions 38 | | ClearOptions 39 | | SelectOptions 40 | | CheckOptions 41 | | HoverOptions 42 | | UploadOptions 43 | | DragOptions 44 | | DoubleClickOptions; 45 | 46 | /** 47 | * 3. Expect Options: These types are used for assertions, Timeouts, etc in tests. 48 | * They are based on the parameters of Playwright's built-in expect methods. 49 | */ 50 | export type TimeoutOption = { timeout?: number }; 51 | export type SoftOption = { soft?: boolean }; 52 | export type MessageOrOptions = string | { message?: string }; 53 | export type ExpectOptions = TimeoutOption & SoftOption & MessageOrOptions; 54 | export type ExpectTextOptions = { 55 | ignoreCase?: boolean; 56 | useInnerText?: boolean; 57 | }; 58 | 59 | export type SwitchPageOptions = { 60 | loadState?: 'load' | 'domcontentloaded' | 'networkidle'; 61 | timeout?: number; 62 | }; 63 | 64 | /** 65 | * 4. Locator Options: These types are used for locating elements on a page. 66 | * They are based on the parameters of Playwright's built-in locator methods. 67 | */ 68 | export type LocatorOptions = Parameters[1] & VisibilityOption; 69 | export type LocatorWaitOptions = { waitForLocator?: boolean } & TimeoutOption; 70 | export type GetByTextOptions = Parameters[1] & VisibilityOption; 71 | export type GetByRoleTypes = Parameters[0] & VisibilityOption; 72 | export type GetByRoleOptions = Parameters[1] & VisibilityOption; 73 | export type GetByLabelOptions = Parameters[1] & VisibilityOption; 74 | export type GetByPlaceholderOptions = Parameters[1] & VisibilityOption; 75 | 76 | export type FrameOptions = Parameters[0]; 77 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/utils/action-utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * action-utils.ts: This module provides a set of utility functions for performing various actions in Playwright tests. 3 | * These actions include navigation, interaction with page elements, handling of dialogs, and more. 4 | */ 5 | import test, { Locator } from '@playwright/test'; 6 | import { getPage } from './page-utils'; 7 | import { 8 | ActionOptions, 9 | CheckOptions, 10 | ClearOptions, 11 | ClickOptions, 12 | DoubleClickOptions, 13 | DragOptions, 14 | FillOptions, 15 | HoverOptions, 16 | PressSequentiallyOptions, 17 | SelectOptions, 18 | StabilityOption, 19 | TimeoutOption, 20 | UploadOptions, 21 | UploadValues, 22 | VisibilityOption, 23 | } from '../types/optional-parameter-types'; 24 | import { SMALL_TIMEOUT, STANDARD_TIMEOUT } from '../constants/timeouts'; 25 | import { getLocator, getVisibleLocator } from './locator-utils'; 26 | import { getDefaultLoadState } from '../constants/loadstate'; 27 | import { waitForElementToBeStable } from './element-utils'; 28 | 29 | /** 30 | * 1. Actions: This section contains functions for interacting with elements on a web page. 31 | * These functions include clicking, filling input fields, typing, clearing input fields, checking and unchecking checkboxes, selecting options in dropdowns, and more. 32 | */ 33 | 34 | /** 35 | * Returns a locator, ensuring visibility by default, with the option to override visibility and stability checks. 36 | * Intended for internal use, this function facilitates retrieving locators under specific conditions— 37 | * visibility (enabled by default) and stability (optional), as per the requirements for further actions. 38 | * Users have the flexibility to adjust these checks via the `options` parameter. 39 | * 40 | * @param input - The selector string or Locator object used to find the element. This serves as the base for locating the element. 41 | * @param options - Optional. Configuration for locator retrieval. Allows overriding the default behavior for visibility and enables stability checks. 42 | * Use `{ onlyVisible: false }` to include non-visible elements and `{ stable: true }` to wait for the element to stabilize. 43 | * @returns A Promise that resolves to a Locator. This locator has been checked for visibility (enabled by default) and, 44 | * if specified in `options`, for stability, ensuring it meets the specified conditions. 45 | */ 46 | async function getLocatorWithStableAndVisibleOptions( 47 | input: string | Locator, 48 | options?: ActionOptions, 49 | ): Promise { 50 | const locator = getVisibleLocator(input, options); 51 | if (options?.stable) await waitForElementToBeStable(input, options); 52 | return locator; 53 | } 54 | 55 | /** 56 | * Clicks on a specified element. 57 | * @param {string | Locator} input - The element to click on. 58 | * @param {ClickOptions} options - The click options. 59 | */ 60 | export async function click(input: string | Locator, options?: ClickOptions): Promise { 61 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 62 | await locator.click(options); 63 | } 64 | 65 | /** 66 | * Clicks on a specified element and waits for navigation. 67 | * @param {string | Locator} input - The element to click on. 68 | * @param {ClickOptions} options - The click options. 69 | */ 70 | export async function clickAndNavigate(input: string | Locator, options?: ClickOptions): Promise { 71 | const timeout = options?.timeout || STANDARD_TIMEOUT; 72 | const elementHandle = await getLocator(input).elementHandle(options); 73 | try { 74 | // Adding 100 ms to the framenavigated timeout prioritizes locator error during click over navigation error, aiding in accurate debugging. 75 | await Promise.all([click(input, options), getPage().waitForEvent('framenavigated', { timeout: timeout + 100 })]); 76 | await getPage().waitForLoadState(options?.loadState || getDefaultLoadState(), { 77 | timeout: timeout, 78 | }); 79 | // Wait for the element to be hidden or stale after navigation. If stale then catch the error and return. 80 | await test.step( 81 | 'Wait for element to be stale/hidden after navigation and ignore any errors in this step', 82 | async () => { 83 | await elementHandle?.waitForElementState('hidden', { timeout }); 84 | }, 85 | { box: true }, 86 | ); 87 | } catch (error) { 88 | if (error instanceof Error && error.name === 'TimeoutError' && error.message.includes('framenavigated')) { 89 | throw new Error(`After the click action, the page did not navigate to a new page\n ${error.message}`); 90 | } else if (error instanceof Error && !error.message.includes('elementHandle.waitForElementState')) { 91 | throw error; 92 | } 93 | } 94 | } 95 | 96 | /** 97 | * Fills a specified element with a value. 98 | * @param {string | Locator} input - The element to fill. 99 | * @param {string} value - The value to fill the element with. 100 | * @param {FillOptions} options - The fill options. 101 | */ 102 | export async function fill(input: string | Locator, value: string, options?: FillOptions): Promise { 103 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 104 | await locator.fill(value, options); 105 | } 106 | 107 | /** 108 | * Fills a specified element with a value and press Enter. 109 | * @param {string | Locator} input - The element to fill. 110 | * @param {string} value - The value to fill the element with. 111 | * @param {FillOptions} options - The fill options. 112 | */ 113 | export async function fillAndEnter(input: string | Locator, value: string, options?: FillOptions): Promise { 114 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 115 | await locator.fill(value, options); 116 | await locator.press('Enter'); 117 | } 118 | 119 | /** 120 | * Fills a specified element with a value and press Tab. 121 | * @param {string | Locator} input - The element to fill. 122 | * @param {string} value - The value to fill the element with. 123 | * @param {FillOptions} options - The fill options. 124 | */ 125 | export async function fillAndTab(input: string | Locator, value: string, options?: FillOptions): Promise { 126 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 127 | await locator.fill(value, options); 128 | await locator.press('Tab'); 129 | } 130 | 131 | /** 132 | * Types a value into a specified element, simulating keystrokes character by character. 133 | * @param {string | Locator} input - The element into which the value will be typed. This can be either a string representing the selector or a Locator object. 134 | * @param {string} value - The string value to be typed into the element, character by character. 135 | * @param {PressSequentiallyOptions} [options] - Optional configuration for the typing action. 136 | */ 137 | export async function pressSequentially( 138 | input: string | Locator, 139 | value: string, 140 | options?: PressSequentiallyOptions, 141 | ): Promise { 142 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 143 | await locator.pressSequentially(value, options); 144 | } 145 | 146 | /** 147 | * Simulates the action of pressing a key or a combination of keys on the page. 148 | * This function is useful for scenarios where you need to simulate key presses like 'Enter', 'Tab', etc on the page.. 149 | * @param {string} key - The key or combination of keys to be pressed. For example, 'Enter', 'Tab', or 'Control+A'. 150 | * @param {PressSequentiallyOptions} [options] - Optional configuration for the key press action. This can include options like delay between key presses. 151 | */ 152 | export async function pressPageKeyboard(key: string, options?: PressSequentiallyOptions): Promise { 153 | await getPage().keyboard.press(key, options); 154 | } 155 | 156 | /** 157 | * Simulates the action of pressing a key or a combination of keys on a specified element. 158 | * This function is useful for scenarios where you need to simulate key presses like 'Enter', 'Tab', etc on a specified element.. 159 | * @param {string} key - The key or combination of keys to be pressed. For example, 'Enter', 'Tab', or 'Control+A'. 160 | * @param {string | Locator} input - The element on which the key press action will be performed. This can be either a string representing the selector or a Locator object. 161 | * @param {PressSequentiallyOptions} [options] - Optional configuration for the key press action. This can include options like delay between key presses. 162 | */ 163 | export async function pressLocatorKeyboard( 164 | input: string | Locator, 165 | key: string, 166 | options?: PressSequentiallyOptions, 167 | ): Promise { 168 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 169 | await locator.press(key, options); 170 | } 171 | 172 | /** 173 | * Clears the value of a specified element. 174 | * @param {string | Locator} input - The element to clear. 175 | * @param {ClearOptions} options - The clear options. 176 | */ 177 | export async function clear(input: string | Locator, options?: ClearOptions): Promise { 178 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 179 | await locator.clear(options); 180 | } 181 | 182 | /** 183 | * Checks a specified checkbox or radio button. 184 | * @param {string | Locator} input - The checkbox or radio button to check. 185 | * @param {CheckOptions} options - The check options. 186 | */ 187 | export async function check(input: string | Locator, options?: CheckOptions): Promise { 188 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 189 | await locator.check(options); 190 | } 191 | 192 | /** 193 | * Unchecks a specified checkbox or radio button. 194 | * @param {string | Locator} input - The checkbox or radio button to uncheck. 195 | * @param {CheckOptions} options - The uncheck options. 196 | */ 197 | export async function uncheck(input: string | Locator, options?: CheckOptions): Promise { 198 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 199 | await locator.uncheck(options); 200 | } 201 | 202 | /** 203 | * Selects an option in a dropdown by its value. 204 | * @param {string | Locator} input - The dropdown to select an option in. 205 | * @param {string} value - The value of the option to select. 206 | * @param {SelectOptions} options - The select options. 207 | */ 208 | export async function selectByValue(input: string | Locator, value: string, options?: SelectOptions): Promise { 209 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 210 | await locator.selectOption({ value: value }, options); 211 | } 212 | 213 | /** 214 | * Selects options in a dropdown by their values (multi select). 215 | * @param {string | Locator} input - The dropdown to select options in. 216 | * @param {Array} value - The values of the options to select. 217 | * @param {SelectOptions} options - The select options. 218 | */ 219 | export async function selectByValues( 220 | input: string | Locator, 221 | value: Array, 222 | options?: SelectOptions, 223 | ): Promise { 224 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 225 | await locator.selectOption(value, options); 226 | } 227 | 228 | /** 229 | * Selects an option in a dropdown by its text. 230 | * @param {string | Locator} input - The dropdown to select an option in. 231 | * @param {string} text - The text of the option to select. 232 | * @param {SelectOptions} options - The select options. 233 | */ 234 | export async function selectByText(input: string | Locator, text: string, options?: SelectOptions): Promise { 235 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 236 | await locator.selectOption({ label: text }, options); 237 | } 238 | 239 | /** 240 | * Selects an option in a dropdown by its index. 241 | * @param {string | Locator} input - The dropdown to select an option in. 242 | * @param {number} index - The index of the option to select. 243 | * @param {SelectOptions} options - The select options. 244 | */ 245 | export async function selectByIndex(input: string | Locator, index: number, options?: SelectOptions): Promise { 246 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 247 | await locator.selectOption({ index: index }, options); 248 | } 249 | 250 | /** 251 | * 2. Alerts: This section contains functions for handling alert dialogs. 252 | * These functions include accepting and dismissing alerts, and getting the text of an alert. 253 | * Note: These functions currently have some repetition and could be optimized by applying the DRY (Don't Repeat Yourself) principle. 254 | */ 255 | 256 | /** 257 | * Accepts an alert dialog. 258 | * @param {string | Locator} input - The element to click to trigger the alert. 259 | * @param {string} [options.promptText] - The text to enter into a prompt dialog. Optional. 260 | * @param {number} [options.timeout] - Maximum time to wait for the alert to appear in milliseconds. Optional. 261 | * @returns {Promise} - The message of the dialog. 262 | */ 263 | export async function acceptAlert( 264 | input: string | Locator, 265 | options?: { promptText?: string } & TimeoutOption, 266 | ): Promise { 267 | const timeoutInMs = options?.timeout || SMALL_TIMEOUT; 268 | const locator = getLocator(input); 269 | let dialogMessage = ''; 270 | 271 | const dialogPromise = getPage() 272 | .waitForEvent('dialog', { timeout: timeoutInMs }) 273 | .then(async dialog => { 274 | dialogMessage = dialog.message(); 275 | return await dialog.accept(options?.promptText); 276 | }) 277 | .catch(() => { 278 | throw new Error(`No dialog appeared after waiting for ${timeoutInMs} ms.`); 279 | }); 280 | 281 | await locator.click(); 282 | await dialogPromise; 283 | return dialogMessage; 284 | } 285 | 286 | /** 287 | * Dismisses an alert dialog. 288 | * @param {string | Locator} input - The element to click to trigger the alert. 289 | * @param {number} [options.timeout] - Maximum time to wait for the alert to appear in milliseconds. Optional. 290 | * @returns {Promise} - The message of the dialog. 291 | */ 292 | export async function dismissAlert(input: string | Locator, options?: TimeoutOption): Promise { 293 | const timeoutInMs = options?.timeout || SMALL_TIMEOUT; 294 | const locator = getLocator(input); 295 | let dialogMessage = ''; 296 | 297 | const dialogPromise = getPage() 298 | .waitForEvent('dialog', { timeout: timeoutInMs }) 299 | .then(async dialog => { 300 | dialogMessage = dialog.message(); 301 | return await dialog.dismiss(); 302 | }) 303 | .catch(() => { 304 | throw new Error(`No dialog appeared after waiting for ${timeoutInMs} ms.`); 305 | }); 306 | 307 | await locator.click(); 308 | await dialogPromise; 309 | return dialogMessage; 310 | } 311 | 312 | /** 313 | * Gets the text of an alert dialog and dismisses the alert triggered. 314 | * @param {string | Locator} input - The element to click to trigger the alert. 315 | * @param {number} [options.timeout] - Maximum time to wait for the alert to appear in milliseconds. Optional. 316 | * @returns {Promise} - The message of the dialog. 317 | */ 318 | 319 | export async function getAlertText(input: string | Locator, options?: TimeoutOption): Promise { 320 | const timeoutInMs = options?.timeout || SMALL_TIMEOUT; 321 | const locator = getLocator(input); 322 | let dialogMessage = ''; 323 | 324 | const dialogPromise = getPage() 325 | .waitForEvent('dialog', { timeout: timeoutInMs }) 326 | .then(async dialog => { 327 | dialogMessage = dialog.message(); 328 | return await dialog.dismiss(); 329 | }) 330 | .catch(() => { 331 | throw new Error(`No dialog appeared after waiting for ${timeoutInMs} ms.`); 332 | }); 333 | 334 | await locator.click(); 335 | await dialogPromise; 336 | return dialogMessage; 337 | } 338 | 339 | // This function will hang when the alert is not triggered 340 | /* export async function getAlertText(input: string | Locator): Promise { 341 | const locator = getLocator(input); 342 | let dialogMessage = ''; 343 | const dialogHandler = (dialog: Dialog) => { 344 | dialogMessage = dialog.message(); 345 | dialog.dismiss().catch(e => console.error('Error dismissing dialog:', e)); 346 | }; 347 | getPage().once('dialog', dialogHandler); 348 | await locator.click(); 349 | getPage().off('dialog', dialogHandler); 350 | return dialogMessage; 351 | } */ 352 | 353 | /** 354 | * Hovers over a specified element. 355 | * @param {string | Locator} input - The element to hover over. 356 | * @param {HoverOptions} options - The hover options. 357 | */ 358 | export async function hover(input: string | Locator, options?: HoverOptions): Promise { 359 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 360 | await locator.hover(options); 361 | } 362 | 363 | /** 364 | * Focuses on a specified element. 365 | * @param {string | Locator} input - The element to focus on. 366 | * @param {TimeoutOption} options - The timeout options. 367 | */ 368 | export async function focus( 369 | input: string | Locator, 370 | options?: TimeoutOption & VisibilityOption & StabilityOption, 371 | ): Promise { 372 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 373 | await locator.focus(options); 374 | } 375 | 376 | /** 377 | * Drags and drops a specified element to a destination. 378 | * @param {string | Locator} input - The element to drag. 379 | * @param {string | Locator} dest - The destination to drop the element at. 380 | * @param {DragOptions} options - The drag options. 381 | */ 382 | export async function dragAndDrop( 383 | input: string | Locator, 384 | dest: string | Locator, 385 | options?: DragOptions, 386 | ): Promise { 387 | const drag = await getLocatorWithStableAndVisibleOptions(input, options); 388 | const drop = await getLocatorWithStableAndVisibleOptions(dest, options); 389 | await drag.dragTo(drop, options); 390 | } 391 | 392 | /** 393 | * Double clicks on a specified element. 394 | * @param {string | Locator} input - The element to double click on. 395 | * @param {DoubleClickOptions} options - The double click options. 396 | */ 397 | export async function doubleClick(input: string | Locator, options?: DoubleClickOptions): Promise { 398 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 399 | await locator.dblclick(options); 400 | } 401 | 402 | /** 403 | * Downloads a file from a specified element. 404 | * @param {string | Locator} input - The element to download the file from. 405 | * @param {string} path - The path to save the downloaded file to. 406 | */ 407 | export async function downloadFile(input: string | Locator, path: string, options?: ClickOptions): Promise { 408 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 409 | const downloadPromise = getPage().waitForEvent('download'); 410 | await click(locator, options); 411 | const download = await downloadPromise; 412 | // Wait for the download process to complete 413 | console.log(await download.path()); 414 | // Save downloaded file somewhere 415 | await download.saveAs(path); 416 | } 417 | 418 | /** 419 | * Uploads files to a specified element. 420 | * @param {string | Locator} input - The element to upload files to. 421 | * @param {UploadValues} path - The files to upload. 422 | * @param {UploadOptions} options - The upload options. 423 | */ 424 | export async function uploadFiles(input: string | Locator, path: UploadValues, options?: UploadOptions): Promise { 425 | const locator = await getLocatorWithStableAndVisibleOptions(input, options); 426 | await locator.setInputFiles(path, options); 427 | } 428 | 429 | /** 430 | * Scrolls a specified element into view. 431 | * @param {string | Locator} input - The element to scroll into view. 432 | * @param {TimeoutOption} options - The timeout options. 433 | */ 434 | export async function scrollLocatorIntoView(input: string | Locator, options?: TimeoutOption): Promise { 435 | const locator = getLocator(input); 436 | await locator.scrollIntoViewIfNeeded(options); 437 | } 438 | 439 | /** 440 | * 3. JS: This section contains functions that use JavaScript to interact with elements on a web page. 441 | * These functions include clicking on an element using JavaScript. 442 | */ 443 | 444 | /** 445 | * Clicks on a specified element using JavaScript execution. 446 | * This method is particularly useful in tests where the standard Playwright `click` function fails to trigger the click event properly. 447 | * It directly invokes the `click` method on the HTMLElement, bypassing any potential issues with CSS, visibility, or DOM events. 448 | * @param {string | Locator} input - The element to click on. Can be a string selector or a Playwright `Locator` object. 449 | * @param {TimeoutOptions} options - Currently the timeout options for evaluate is not taking effect. 450 | */ 451 | export async function clickByJS(input: string | Locator, options?: TimeoutOption): Promise { 452 | const locator = getLocator(input); 453 | await locator.evaluate((el: HTMLElement) => el.click(), options); 454 | } 455 | 456 | /** 457 | * Clears the value of an input element and triggers an input event. 458 | * This method is particularly useful in tests where the standard clear or fill functions fail to clear the text. 459 | * The `input` event is dispatched with the `bubbles` option set to true, 460 | * allowing the event to bubble up through the DOM, which can trigger event 461 | * listeners attached to parent elements, ensuring the application reacts as expected. 462 | * @param {string | Locator} input - The element whose input value is to be cleared. Can be a string selector or a Playwright Locator. 463 | * @param {TimeoutOptions} options - Currently the timeout options for evaluate is not taking effect. 464 | */ 465 | export async function clearByJS(input: string | Locator, options?: TimeoutOption): Promise { 466 | const locator = getLocator(input); 467 | await locator.evaluate(element => { 468 | (element as HTMLInputElement).value = ''; 469 | element.dispatchEvent(new Event('input', { bubbles: true })); 470 | element.dispatchEvent(new Event('change', { bubbles: true })); 471 | }, options); 472 | } 473 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/utils/api-utils.ts: -------------------------------------------------------------------------------- 1 | import { getPage } from './page-utils'; 2 | import { APIRequestContext, APIResponse } from '@playwright/test'; 3 | 4 | /** 5 | * Retrieves the APIRequestContext from the current page equivalent to request from playwright fixture. 6 | * @returns {APIRequestContext} The Playwright API request context. 7 | */ 8 | export function getAPIRequestContext(): APIRequestContext { 9 | return getPage().request; 10 | } 11 | 12 | /** 13 | * Performs a GET request to the specified URL. 14 | * @param {string} url - The URL to send the GET request to. 15 | * @param {Parameters[1]} options - Optional parameters for the GET request. 16 | * @returns {Promise} A promise that resolves with the API response. 17 | */ 18 | export async function getRequest(url: string, options?: Parameters[1]): Promise { 19 | return await getAPIRequestContext().get(url, options); 20 | } 21 | 22 | /** 23 | * Performs a POST request to the specified URL. 24 | * @param {string} url - The URL to send the POST request to. 25 | * @param {Parameters[1]} options - Optional parameters for the POST request. 26 | * @returns {Promise} A promise that resolves with the API response. 27 | */ 28 | export async function postRequest( 29 | url: string, 30 | options?: Parameters[1], 31 | ): Promise { 32 | return await getAPIRequestContext().post(url, options); 33 | } 34 | 35 | /** 36 | * Performs a PUT request to the specified URL. 37 | * @param {string} url - The URL to send the PUT request to. 38 | * @param {Parameters[1]} options - Optional parameters for the PUT request. 39 | * @returns {Promise} A promise that resolves with the API response. 40 | */ 41 | export async function putRequest(url: string, options?: Parameters[1]): Promise { 42 | return await getAPIRequestContext().put(url, options); 43 | } 44 | 45 | /** 46 | * Performs a DELETE request to the specified URL. 47 | * @param {string} url - The URL to send the DELETE request to. 48 | * @param {Parameters[1]} options - Optional parameters for the DELETE request. 49 | * @returns {Promise} A promise that resolves with the API response. 50 | */ 51 | export async function deleteRequest( 52 | url: string, 53 | options?: Parameters[1], 54 | ): Promise { 55 | return await getAPIRequestContext().delete(url, options); 56 | } 57 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/utils/assert-utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * AssertUtils.ts: This module contains utility functions for assertions in tests. 3 | * All expect assertions will dynamically wait until either the expect timeout specified in the 4 | * playwright.config is reached or the condition becomes true. 5 | * @module AssertUtils 6 | */ 7 | 8 | import { Expect, Locator, TestInfo, expect } from '@playwright/test'; 9 | import { ExpectOptions, ExpectTextOptions, SoftOption, TimeoutOption } from '../types/optional-parameter-types'; 10 | import { getLocator } from './locator-utils'; 11 | import { getAllPages, getPage } from './page-utils'; 12 | import { getAlertText } from './action-utils'; 13 | 14 | /** 15 | * Returns an Expect object configured with the given soft option. 16 | * @param {SoftOption} options - The soft option to configure the Expect object with. 17 | * @returns {Expect} - The configured Expect object. 18 | */ 19 | function getExpectWithSoftOption(options?: SoftOption): Expect { 20 | return expect.configure({ soft: options?.soft }); 21 | } 22 | 23 | /** 24 | * Returns a Locator object and an Expect object configured with the given soft option. 25 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 26 | * @param {SoftOption} options - The soft option to configure the Expect object with. 27 | * @returns {Object} - An object containing the Locator and Expect objects. 28 | */ 29 | function getLocatorAndAssert(input: string | Locator, options?: SoftOption): { locator: Locator; assert: Expect } { 30 | const locator = getLocator(input); 31 | const assert = getExpectWithSoftOption(options); 32 | return { locator, assert }; 33 | } 34 | 35 | /** 36 | * Use this function to assert all the soft assertions. 37 | * @param {TestInfo} testInfo - The TestInfo object containing the test's information. 38 | */ 39 | export function assertAllSoftAssertions(testInfo: TestInfo): void { 40 | expect(testInfo.errors).toHaveLength(0); 41 | } 42 | 43 | /** 44 | * 1. Locator Assertions: This section contains functions that perform assertions on specific locators. 45 | * These functions check for various conditions such as visibility, presence in the DOM, text content, etc. 46 | */ 47 | 48 | /** 49 | * Asserts that the given element is not present in the DOM or is Hidden. 50 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 51 | * @param {ExpectOptions} options - The options to pass to the expect function. 52 | */ 53 | export async function expectElementToBeHidden(input: string | Locator, options?: ExpectOptions): Promise { 54 | const { locator, assert } = getLocatorAndAssert(input, options); 55 | await assert(locator, options).toBeHidden(options); 56 | } 57 | 58 | /** 59 | * Asserts that the given element is present in the DOM and visible. 60 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 61 | * @param {ExpectOptions} options - The options to pass to the expect function. 62 | */ 63 | export async function expectElementToBeVisible(input: string | Locator, options?: ExpectOptions): Promise { 64 | const { locator, assert } = getLocatorAndAssert(input, options); 65 | await assert(locator, options).toBeVisible(options); 66 | } 67 | 68 | /** 69 | * Asserts that the given element is present in the DOM. 70 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 71 | * @param {ExpectOptions} options - The options to pass to the expect function. 72 | */ 73 | export async function expectElementToBeAttached(input: string | Locator, options?: ExpectOptions): Promise { 74 | const { locator, assert } = getLocatorAndAssert(input, options); 75 | await assert(locator, options).toBeAttached(options); 76 | } 77 | 78 | /** 79 | * Asserts that the given element is present in the DOM and visible in the viewport of the page. 80 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 81 | * @param {ExpectOptions} options - The options to pass to the expect function. 82 | */ 83 | export async function expectElementToBeInViewport(input: string | Locator, options?: ExpectOptions): Promise { 84 | const { locator, assert } = getLocatorAndAssert(input, options); 85 | await assert(locator, options).toBeInViewport(options); 86 | } 87 | 88 | /** 89 | * Asserts that the given element is not present in the DOM or not visible in the viewport of the page. 90 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 91 | * @param {ExpectOptions} options - The options to pass to the expect function. 92 | */ 93 | export async function expectElementNotToBeInViewport(input: string | Locator, options?: ExpectOptions): Promise { 94 | const { locator, assert } = getLocatorAndAssert(input, options); 95 | await assert(locator, options).not.toBeInViewport(options); 96 | } 97 | 98 | /** 99 | * Asserts that the given element is checked. 100 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 101 | * @param {ExpectOptions} options - The options to pass to the expect function. 102 | */ 103 | export async function expectElementToBeChecked(input: string | Locator, options?: ExpectOptions): Promise { 104 | const { locator, assert } = getLocatorAndAssert(input, options); 105 | await assert(locator, options).toBeChecked(options); 106 | } 107 | 108 | /** 109 | * Asserts that the given element is not checked. 110 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 111 | * @param {ExpectOptions} options - The options to pass to the expect function. 112 | */ 113 | export async function expectElementNotToBeChecked(input: string | Locator, options?: ExpectOptions): Promise { 114 | const { locator, assert } = getLocatorAndAssert(input, options); 115 | await assert(locator, options).not.toBeChecked(options); 116 | } 117 | 118 | /** 119 | * Asserts that the given element is disabled. 120 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 121 | * @param {ExpectOptions} options - The options to pass to the expect function. 122 | */ 123 | export async function expectElementToBeDisabled(input: string | Locator, options?: ExpectOptions): Promise { 124 | const { locator, assert } = getLocatorAndAssert(input, options); 125 | await assert(locator, options).toBeDisabled(options); 126 | } 127 | 128 | /** 129 | * Asserts that the given element is enabled. 130 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 131 | * @param {ExpectOptions} options - The options to pass to the expect function. 132 | */ 133 | export async function expectElementToBeEnabled(input: string | Locator, options?: ExpectOptions): Promise { 134 | const { locator, assert } = getLocatorAndAssert(input, options); 135 | await assert(locator, options).toBeEnabled(options); 136 | } 137 | 138 | /** 139 | * Asserts that the given element is editable. 140 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 141 | * @param {ExpectOptions} options - The options to pass to the expect function. 142 | */ 143 | export async function expectElementToBeEditable(input: string | Locator, options?: ExpectOptions): Promise { 144 | const { locator, assert } = getLocatorAndAssert(input, options); 145 | await assert(locator, options).toBeEditable(options); 146 | } 147 | 148 | /** 149 | * Asserts that the element equals the provided string or string array or regular expression. 150 | * @param {string | Locator} input - Either a string (selector) or a Locator object from where we retrieve the text to assert. 151 | * @param {string | string[] | RegExp} text - The string, string array or regular expression to match against the element's text. 152 | * @param {ExpectOptions & ExpectTextOptions} options - The options to pass to the expect function. 153 | */ 154 | export async function expectElementToHaveText( 155 | input: string | Locator, 156 | text: string | RegExp | Array, 157 | options?: ExpectOptions & ExpectTextOptions, 158 | ): Promise { 159 | const { locator, assert } = getLocatorAndAssert(input, options); 160 | await assert(locator, options).toHaveText(text, options); 161 | } 162 | 163 | /** 164 | * Asserts that the element does not equal the provided string or string array or regular expression. 165 | * @param {string | Locator} input - Either a string (selector) or a Locator object from where we retrieve the text to assert. 166 | * @param {string | string[] | RegExp} text - The string, string array or regular expression to match against the element's text. 167 | * @param {ExpectOptions & ExpectTextOptions} options - The options to pass to the expect function. 168 | */ 169 | export async function expectElementNotToHaveText( 170 | input: string | Locator, 171 | text: string | RegExp | Array, 172 | options?: ExpectOptions & ExpectTextOptions, 173 | ): Promise { 174 | const { locator, assert } = getLocatorAndAssert(input, options); 175 | await assert(locator, options).not.toHaveText(text, options); 176 | } 177 | 178 | /** 179 | * Asserts that the element contains the provided string or string array or regular expression. 180 | * @param {string | Locator} input - Either a string (selector) or a Locator object from where we retrieve the text to assert. 181 | * @param {string | string[] | RegExp} text - The string, string array or regular expression to match against the element's text. 182 | * @param {ExpectOptions & ExpectTextOptions} options - The options to pass to the expect function. 183 | */ 184 | export async function expectElementToContainText( 185 | input: string | Locator, 186 | text: string | RegExp | Array, 187 | options?: ExpectOptions & ExpectTextOptions, 188 | ): Promise { 189 | const { locator, assert } = getLocatorAndAssert(input, options); 190 | await assert(locator, options).toContainText(text, options); 191 | } 192 | 193 | /** 194 | * Asserts that the element does not contain the provided string or string array or regular expression. 195 | * @param {string | Locator} input - Either a string (selector) or a Locator object from where we retrieve the text to assert. 196 | * @param {string | string[] | RegExp} text - The string, string array or regular expression to match against the element's text. 197 | * @param {ExpectOptions & ExpectTextOptions} options - The options to pass to the expect function. 198 | */ 199 | export async function expectElementNotToContainText( 200 | input: string | Locator, 201 | text: string | RegExp | Array, 202 | options?: ExpectOptions & ExpectTextOptions, 203 | ): Promise { 204 | const { locator, assert } = getLocatorAndAssert(input, options); 205 | await assert(locator, options).not.toContainText(text, options); 206 | } 207 | 208 | /** 209 | * Asserts that the given element points to an input text box with the given text or Regex. 210 | * @param {string | Locator} input - Either a string (selector) or a Locator object from where we retrieve the input value to assert. 211 | * @param {string | RegExp} text - The string or regular expression to match against the element's value. 212 | * @param {ExpectOptions} options - The options to pass to the expect function. 213 | */ 214 | export async function expectElementToHaveValue( 215 | input: string | Locator, 216 | text: string | RegExp, 217 | options?: ExpectOptions, 218 | ): Promise { 219 | const { locator, assert } = getLocatorAndAssert(input, options); 220 | await assert(locator, options).toHaveValue(text, options); 221 | } 222 | 223 | /** 224 | * Asserts that the given element points to a multi-select/combobox (i.e. a select with the multiple attribute) and the specified values are selected. 225 | * @param {string | Locator} input - Either a string (selector) or a Locator object from where we retrieve the input value to assert. 226 | * @param {Array} text - The array of strings or regular expressions to match against the element's values. 227 | * @param {ExpectOptions} options - The options to pass to the expect function. 228 | */ 229 | export async function expectElementToHaveValues( 230 | input: string | Locator, 231 | text: Array, 232 | options?: ExpectOptions, 233 | ): Promise { 234 | const { locator, assert } = getLocatorAndAssert(input, options); 235 | await assert(locator, options).toHaveValues(text, options); 236 | } 237 | 238 | /** 239 | * Asserts that the given element points to an empty editable element or to a DOM node that has no text. 240 | * @param {string | Locator} input - Either a string (selector) or a Locator object from where we retrieve the input value to assert. 241 | * @param {ExpectOptions} options - The options to pass to the expect function. 242 | */ 243 | export async function expectElementValueToBeEmpty(input: string | Locator, options?: ExpectOptions): Promise { 244 | const { locator, assert } = getLocatorAndAssert(input, options); 245 | await assert(locator, options).toBeEmpty(options); 246 | } 247 | 248 | /** 249 | * Asserts that the given element points to a non-empty editable element or to a DOM node that has text. 250 | * @param {string | Locator} input - Either a string (selector) or a Locator object from where we retrieve the input value to assert. 251 | * @param {ExpectOptions} options - The options to pass to the expect function. 252 | */ 253 | export async function expectElementValueNotToBeEmpty(input: string | Locator, options?: ExpectOptions): Promise { 254 | const { locator, assert } = getLocatorAndAssert(input, options); 255 | await assert(locator, options).not.toBeEmpty(options); 256 | } 257 | 258 | /** 259 | * Asserts that an element has an attribute with the given value. 260 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 261 | * @param {string} attribute - The attribute to check for. 262 | * @param {string | RegExp} value - The value to match against the attribute. 263 | * @param {ExpectOptions} options - The options to pass to the expect function. 264 | */ 265 | export async function expectElementToHaveAttribute( 266 | input: string | Locator, 267 | attribute: string, 268 | value: string | RegExp, 269 | options?: ExpectOptions, 270 | ): Promise { 271 | const { locator, assert } = getLocatorAndAssert(input, options); 272 | await assert(locator, options).toHaveAttribute(attribute, value, options); 273 | } 274 | 275 | /** 276 | * Asserts that an element has an attribute which contains the given value. 277 | * @param {string | Locator} input - Either a string (selector) or a Locator object. 278 | * @param {string} attribute - The attribute to check for. 279 | * @param {string | RegExp} value - The value to match against the attribute. 280 | * @param {ExpectOptions} options - The options to pass to the expect function. 281 | */ 282 | export async function expectElementToContainAttribute( 283 | input: string | Locator, 284 | attribute: string, 285 | value: string | RegExp, 286 | options?: ExpectOptions, 287 | ): Promise { 288 | const { locator, assert } = getLocatorAndAssert(input, options); 289 | await assert(locator, options).toHaveAttribute(attribute, new RegExp(value), options); 290 | } 291 | 292 | /** 293 | * Asserts that the given element has the specified count. 294 | * @param {string | Locator} input - Either a string (selector) or a Locator object to get the element count. 295 | * @param {number} count - The count to match against the element's count. 296 | * @param {ExpectOptions} options - The options to pass to the expect function. 297 | */ 298 | export async function expectElementToHaveCount( 299 | input: string | Locator, 300 | count: number, 301 | options?: ExpectOptions, 302 | ): Promise { 303 | const { locator, assert } = getLocatorAndAssert(input, options); 304 | await assert(locator, options).toHaveCount(count, options); 305 | } 306 | 307 | /** 308 | * 2. Page Assertions: This section contains functions that perform assertions on the entire page. 309 | * These functions check for conditions such as URL, title, etc. 310 | */ 311 | 312 | /** 313 | * Asserts that the current page URL matches exactly the provided URL or regular expression. 314 | * @param {string | RegExp} urlOrRegExp - The URL or regular expression to match against the current page URL. 315 | */ 316 | export async function expectPageToHaveURL(urlOrRegExp: string | RegExp, options?: ExpectOptions): Promise { 317 | const assert = getExpectWithSoftOption(options); 318 | await assert(getPage()).toHaveURL(urlOrRegExp, options); 319 | } 320 | 321 | /** 322 | * Asserts that the current page URL contains the provided URL. 323 | * @param {string } url - The URL to match against the current page URL. 324 | */ 325 | export async function expectPageToContainURL(url: string, options?: ExpectOptions): Promise { 326 | const assert = getExpectWithSoftOption(options); 327 | await assert(getPage()).toHaveURL(new RegExp(url), options); 328 | } 329 | 330 | /** 331 | * This method will be used for future stories validations Asserts that the current page Title 332 | * matches exactly the provided title or regular expression. 333 | * @param {string | RegExp} titleOrRegExp - The title or regular expression to match against the current page title. 334 | * @param {ExpectOptions} options - The options to pass to the expect function. 335 | */ 336 | export async function expectPageToHaveTitle(titleOrRegExp: string | RegExp, options?: ExpectOptions): Promise { 337 | const assert = getExpectWithSoftOption(options); 338 | await assert(getPage()).toHaveTitle(titleOrRegExp, options); 339 | } 340 | 341 | /** 342 | * Asserts that the number of pages in the current context is equal to the specified size. 343 | * 344 | * @param {number} numberOfPages - The expected number of pages. 345 | * @param {SoftOption} [options] - Optional settings for the Soft assertion. 346 | * @returns {void} 347 | */ 348 | export function expectPageSizeToBeEqualTo(numberOfPages: number, options?: SoftOption): void { 349 | const assert = getExpectWithSoftOption(options); 350 | assert(getAllPages().length).toEqual(numberOfPages); 351 | } 352 | 353 | /** 354 | * Asserts that the text from an alert equals the provided string. 355 | * @param {string | Locator} input - Either a string (selector) or a Locator object from which to trigger the alert and retrieve its text. 356 | * @param {string} text - The string to match against the alert's text. 357 | * @param {ExpectOptions & TimeoutOption} [options] - Optional. The options to configure the expectation, including timeout settings. 358 | */ 359 | 360 | export async function expectAlertToHaveText( 361 | input: string | Locator, 362 | text: string, 363 | options?: ExpectOptions & TimeoutOption, 364 | ): Promise { 365 | const { locator, assert } = getLocatorAndAssert(input, options); 366 | assert(await getAlertText(locator, options)).toBe(text); 367 | } 368 | 369 | /** 370 | * Asserts that the text from an alert matches the provided string or regular expression. 371 | * @param {string | Locator} input - Either a string (selector) or a Locator object from which to trigger the alert and retrieve its text. 372 | * @param {string | RegExp} text - The string or RegExp to test against the alert's text. If a string is provided, the alert text should contain the expected text. If a RegExp is provided, the text must match the pattern. 373 | * @param {ExpectOptions & TimeoutOption} [options] - Optional. The options to configure the expectation, including timeout settings. 374 | */ 375 | 376 | export async function expectAlertToMatchText( 377 | input: string | Locator, 378 | text: string | RegExp, 379 | options?: ExpectOptions & TimeoutOption, 380 | ): Promise { 381 | const { locator, assert } = getLocatorAndAssert(input, options); 382 | assert(await getAlertText(locator, options)).toMatch(text); 383 | } 384 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/utils/element-utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * element-utils.ts: This module provides utility functions for retrieving text from web elements in web page and conditional statements with in Playwright. 3 | * These utilities include a variety of functions for retrieving text, input values, URLs, and checking conditions such as 4 | * whether an element is visible or checked. It provides a layer of abstraction over Playwright's built-in methods for 5 | * interacting with elements, making it easier to perform common tasks and checks on web elements. 6 | */ 7 | 8 | import { Locator } from '@playwright/test'; 9 | import { LocatorWaitOptions, TimeoutOption } from '../types/optional-parameter-types'; 10 | import { getAllLocators, getLocator } from './locator-utils'; 11 | import { SMALL_TIMEOUT } from '../constants/timeouts'; 12 | import { wait } from './page-utils'; 13 | import { logger } from '../setup'; 14 | import { test } from '@playwright/test'; 15 | 16 | /** 17 | * 1. Retreiving Data: Use these functions to retrieve text, values, and counts from web elements. 18 | * These functions can also be used in conditional statements to check the state of web elements. 19 | * These functions are not intended for use in assertions, unless the built-in Playwright assertions do not meet your criteria. 20 | */ 21 | 22 | /** 23 | * Returns the inner text of a Locator object. 24 | * @param {string | Locator} input - The input to create the Locator from. 25 | * @param {TimeoutOption} [options] - Optional timeout options. 26 | * @returns {Promise} - The inner text of the Locator. 27 | */ 28 | export async function getText(input: string | Locator, options?: TimeoutOption): Promise { 29 | const locator = getLocator(input); 30 | return (await locator.innerText(options)).trim(); 31 | } 32 | 33 | /** 34 | * Returns the inner text of all Locator objects. 35 | * @param {string | Locator} input - The input to create the Locator from. 36 | * @returns {Promise>} - The inner text of all Locator objects. 37 | */ 38 | export async function getAllTexts(input: string | Locator, options?: LocatorWaitOptions): Promise> { 39 | await waitForFirstElementToBeAttached(input, options); 40 | const locator = getLocator(input); 41 | return (await locator.allInnerTexts()).map(text => text.trim()); 42 | } 43 | 44 | /** 45 | * Returns the input value of a Locator object. 46 | * @param {string | Locator} input - The input to create the Locator from. 47 | * @param {TimeoutOption} [options] - Optional timeout options. 48 | * @returns {Promise} - The input value of the Locator. 49 | */ 50 | export async function getInputValue(input: string | Locator, options?: TimeoutOption): Promise { 51 | const locator = getLocator(input); 52 | return (await locator.inputValue(options)).trim(); 53 | } 54 | 55 | /** 56 | * Returns the input values of all Locator objects. 57 | * @param {string | Locator} input - The input to create the Locator from. 58 | * @param {TimeoutOption} [options] - Optional timeout options. 59 | * @returns {Promise>} - The input values of all Locator objects. 60 | */ 61 | export async function getAllInputValues(input: string | Locator, options?: TimeoutOption): Promise> { 62 | const locators = await getAllLocators(input); 63 | return Promise.all(locators.map(locator => getInputValue(locator, options))); 64 | } 65 | 66 | /** 67 | * Returns the attribute of a Locator object. 68 | * @param {string | Locator} input - The input to create the Locator from. 69 | * @param {string} attributeName - The name of the attribute to get. 70 | * @param {TimeoutOption} [options] - Optional timeout options. 71 | * @returns {Promise} - The attribute of the Locator if present or null if absent. 72 | */ 73 | export async function getAttribute( 74 | input: string | Locator, 75 | attributeName: string, 76 | options?: TimeoutOption, 77 | ): Promise { 78 | const locator = getLocator(input); 79 | return (await locator.getAttribute(attributeName, options))?.trim() || null; 80 | } 81 | 82 | /** 83 | * Returns the count of Locator objects. 84 | * @param {string | Locator} input - The input to create the Locator from. 85 | * @param {TimeoutOption} [options] - Optional timeout options. 86 | * @returns {Promise} - The count of the Locator objects. 87 | */ 88 | export async function getLocatorCount(input: string | Locator, options?: LocatorWaitOptions): Promise { 89 | try { 90 | return (await getAllLocators(input, options)).length; 91 | } catch (error) { 92 | console.log(`getLocatorCount- ${error instanceof Error ? error.message : String(error)}`); 93 | } 94 | return 0; 95 | } 96 | 97 | /** 98 | * 2. Conditions: Use these checks within conditional statements. 99 | * They are not intended for use in assertions, unless the built-in Playwright assertions do not meet your criteria. 100 | */ 101 | 102 | /** 103 | * Checks if a Locator object is attached to DOM. 104 | * @param {string | Locator} input - The input to create the Locator from. 105 | * @param {TimeoutOption} [options] - Optional timeout options. 106 | * @returns {Promise} - True if the Locator is attached, false otherwise. 107 | */ 108 | export async function isElementAttached(input: string | Locator, options?: TimeoutOption): Promise { 109 | const locator = getLocator(input); // Assuming getLocator returns a Playwright Locator 110 | const timeoutInMs = options?.timeout || SMALL_TIMEOUT; 111 | 112 | try { 113 | await locator.waitFor({ state: 'attached', timeout: timeoutInMs }); 114 | return true; 115 | } catch (error) { 116 | console.log(`isElementAttached- ${error instanceof Error ? error.message : String(error)}`); 117 | return false; 118 | } 119 | } 120 | 121 | /** 122 | * Checks if a Locator object is attached to DOM and is visible. 123 | * @param {string | Locator} input - The input to create the Locator from. 124 | * @param {TimeoutOption} [options] - Optional timeout options. 125 | * @returns {Promise} - True if the Locator is visible, false otherwise. 126 | */ 127 | export async function isElementVisible(input: string | Locator, options?: TimeoutOption): Promise { 128 | const locator = getLocator(input); 129 | const timeoutInMs = options?.timeout || SMALL_TIMEOUT; 130 | const startTime = Date.now(); 131 | try { 132 | while (Date.now() - startTime < timeoutInMs) { 133 | if (await locator.isVisible(options)) { 134 | return true; 135 | } 136 | await new Promise(resolve => setTimeout(resolve, 100)); 137 | } 138 | } catch (error) { 139 | console.log(`isElementVisible- ${error instanceof Error ? error.message : String(error)}`); 140 | } 141 | return false; 142 | } 143 | 144 | /** 145 | * Checks if a Locator object is hidden or not present in DOM. 146 | * @param {string | Locator} input - The input to create the Locator from. 147 | * @param {TimeoutOption} [options] - Optional timeout options. 148 | * @returns {Promise} - True if the Locator is hidden, false otherwise. 149 | */ 150 | export async function isElementHidden(input: string | Locator, options?: TimeoutOption): Promise { 151 | const locator = getLocator(input); 152 | const timeoutInMs = options?.timeout || SMALL_TIMEOUT; 153 | const startTime = Date.now(); 154 | try { 155 | while (Date.now() - startTime < timeoutInMs) { 156 | if (await locator.isHidden(options)) { 157 | return true; 158 | } 159 | await new Promise(resolve => setTimeout(resolve, 100)); 160 | } 161 | } catch (error) { 162 | console.log(`isElementHidden- ${error instanceof Error ? error.message : String(error)}`); 163 | } 164 | return false; 165 | } 166 | 167 | /** 168 | * Checks if a Locator object is checked. 169 | * @param {string | Locator} input - The input to create the Locator from. 170 | * @param {TimeoutOption} [options] - Optional timeout options. 171 | * @returns {Promise} - True if the Locator is checked, false otherwise. 172 | */ 173 | export async function isElementChecked(input: string | Locator, options?: TimeoutOption): Promise { 174 | try { 175 | if (await isElementVisible(input, options)) { 176 | return await getLocator(input).isChecked(options); 177 | } 178 | } catch (error) { 179 | console.log(`isElementChecked- ${error instanceof Error ? error.message : String(error)}`); 180 | } 181 | return false; 182 | } 183 | 184 | /** 185 | * Waits for an element to be stable on the page. 186 | * @param input - The element or locator to wait for. 187 | * @param options - Optional timeout options. 188 | * @returns A promise that resolves to a boolean indicating if the element is stable. 189 | */ 190 | export async function waitForElementToBeStable(input: string | Locator, options?: TimeoutOption): Promise { 191 | let result = false; 192 | await test.step('waitForElementToBeStable', async () => { 193 | const locator = getLocator(input); 194 | const maxWaitTime = options?.timeout || SMALL_TIMEOUT; 195 | let stableCounter = 0; 196 | 197 | const initialBoundingBox = await locator.boundingBox(); 198 | let lastX: number | null = initialBoundingBox?.x || null; 199 | let lastY: number | null = initialBoundingBox?.y || null; 200 | 201 | const startTime = Date.now(); 202 | await wait(200); 203 | 204 | while (Date.now() - startTime < maxWaitTime) { 205 | const { x, y } = (await locator.boundingBox()) || { x: null, y: null }; 206 | 207 | if (x === lastX && y === lastY) { 208 | stableCounter++; 209 | if (stableCounter >= 3) { 210 | result = true; 211 | break; 212 | } 213 | await wait(100); 214 | } else { 215 | // stableCounter = 0; 216 | await wait(200); 217 | } 218 | 219 | lastX = x; 220 | lastY = y; 221 | } 222 | 223 | if (!result) { 224 | logger.error('Max wait time exceeded. Element is not stable.'); 225 | } 226 | }); 227 | return result; 228 | } 229 | 230 | /** 231 | * Waits for an element to be visible on the page. 232 | * @param input - The element or locator to wait for. 233 | * @param options - Optional timeout options. 234 | * @returns A promise that resolves when the element is visible. 235 | */ 236 | export async function waitForElementToBeVisible(input: string | Locator, options?: TimeoutOption): Promise { 237 | const locator = getLocator(input); 238 | await locator.waitFor({ state: 'visible', timeout: options?.timeout || SMALL_TIMEOUT }); 239 | } 240 | 241 | /** 242 | * Waits for an element to be hidden on the page or detached from the DOM. 243 | * @param input - The element or locator to wait for. 244 | * @param options - Optional timeout options. 245 | * @returns A promise that resolves when the element is hidden. 246 | */ 247 | export async function waitForElementToBeHidden(input: string | Locator, options?: TimeoutOption): Promise { 248 | const locator = getLocator(input); 249 | await locator.waitFor({ state: 'hidden', timeout: options?.timeout || SMALL_TIMEOUT }); 250 | } 251 | 252 | /** 253 | * Waits for an element to be attached to the DOM. 254 | * @param input - The element or locator to wait for. 255 | * @param options - Optional timeout options. 256 | * @returns A promise that resolves when the element is attached to the DOM. 257 | */ 258 | export async function waitForElementToBeAttached(input: string | Locator, options?: TimeoutOption): Promise { 259 | const locator = getLocator(input); 260 | await locator.waitFor({ state: 'attached', timeout: options?.timeout || SMALL_TIMEOUT }); 261 | } 262 | 263 | /** 264 | * Ensures that the first element of the locator is attached to the DOM if the waitForLocator option is true. 265 | * @param {string | Locator} input - The input to create the Locator from. It can be a string or a Locator. 266 | * @param {LocatorWaitOptions} [options] - Optional parameters for Locator waiting options. 267 | * @returns {Promise} - A promise that resolves when the element is attached or immediately if waitForLocator is false. 268 | */ 269 | export async function waitForFirstElementToBeAttached( 270 | input: string | Locator, 271 | options?: LocatorWaitOptions, 272 | ): Promise { 273 | const locator = getLocator(input); 274 | const waitForLocator = options?.waitForLocator ?? true; 275 | // If waitForLocator is true, wait for the element to be attached before returning the locators 276 | if (waitForLocator) { 277 | await waitForElementToBeAttached(locator.first(), options); 278 | } 279 | } 280 | 281 | /** 282 | * Waits for an element to be detached from the DOM. 283 | * @param input - The element or locator to wait for. 284 | * @param options - Optional timeout options. 285 | * @returns A promise that resolves when the element is detached from the DOM. 286 | */ 287 | export async function waitForElementToBeDetached(input: string | Locator, options?: TimeoutOption): Promise { 288 | const locator = getLocator(input); 289 | await locator.waitFor({ state: 'detached', timeout: options?.timeout || SMALL_TIMEOUT }); 290 | } 291 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * as ActionUtils from './action-utils'; 2 | export * from './action-utils'; 3 | export * as AssertUtils from './assert-utils'; 4 | export * from './assert-utils'; 5 | export * as ElementUtils from './element-utils'; 6 | export * from './element-utils'; 7 | export * as LocatorUtils from './locator-utils'; 8 | export * from './locator-utils'; 9 | export * as PageUtils from './page-utils'; 10 | export * from './page-utils'; 11 | export * as APIUtils from './api-utils'; 12 | export * from './api-utils'; 13 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/utils/locator-utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * locator-utils.ts: This module provides utility functions for handling and manipulating locators in Playwright. 3 | * These utilities make it easier to interact with elements on the page, providing a layer of abstraction over Playwright's built-in locator methods. 4 | */ 5 | 6 | import { Frame, FrameLocator, Locator, selectors } from '@playwright/test'; 7 | import { getPage } from './page-utils'; 8 | import { 9 | FrameOptions, 10 | GetByPlaceholderOptions, 11 | GetByRoleOptions, 12 | GetByRoleTypes, 13 | GetByTextOptions, 14 | LocatorOptions, 15 | LocatorWaitOptions, 16 | } from '../types/optional-parameter-types'; 17 | import { defaultVisibleOnlyOption } from '../constants'; 18 | import { waitForFirstElementToBeAttached } from './element-utils'; 19 | 20 | /** 21 | * 1. Locators: This section contains functions and definitions related to locators. 22 | * Locators are used to find and interact with elements on the page. 23 | */ 24 | 25 | /** 26 | * Returns a Locator object based on the input provided. 27 | * @param {string | Locator} input - The input to create the Locator from. 28 | * @param {LocatorOptions} options - Optional parameters for the Locator. 29 | * @returns {Locator} - The created Locator object. 30 | */ 31 | export function getLocator(input: string | Locator, options?: LocatorOptions): Locator { 32 | const locator = typeof input === 'string' ? getPage().locator(input, options) : input; 33 | return options?.onlyVisible ? locator.locator('visible=true') : locator; 34 | } 35 | 36 | /** 37 | * Returns a locator pointing to only visible element based on the input provided. 38 | * By default, it returns locators that are visible. This behavior can be customized 39 | * via the `options` parameter, allowing for more specific locator configurations. 40 | * 41 | * @param input - The selector string or Locator object to identify the element. 42 | * @param options - Optional. Configuration options for the locator. Use `{ onlyVisible: false }` 43 | * to include non-visible elements in the locator's search criteria. 44 | * @returns A `Locator` instance pointing to an element that matches the specified 45 | * criteria, prioritizing visibility unless overridden by `options`. 46 | */ 47 | export function getVisibleLocator(input: string | Locator, options?: LocatorOptions): Locator { 48 | return getLocator(input, { ...defaultVisibleOnlyOption, ...options }); 49 | } 50 | 51 | /** 52 | * Returns a Locator object with a specific testId. The global testId attribute is set in the playwright.config.ts file with default value as 'data-testid' if not set explicitly, but can be overridden by providing an attributeName. 53 | * @param {string | RegExp} testId - The testId to create the Locator from. 54 | * @param {string} [attributeName] - Optional attribute name for the testId. If provided, this will override the default 'testId' attribute value set in the playwright.config.ts file only for this instance. 55 | * @returns {Locator} - The created Locator object. 56 | */ 57 | export function getLocatorByTestId(testId: string | RegExp, attributeName?: string): Locator { 58 | if (attributeName) { 59 | selectors.setTestIdAttribute(attributeName); 60 | } 61 | return getPage().getByTestId(testId); 62 | } 63 | 64 | /** 65 | * Returns a Locator object with a specific text. 66 | * @param {string | RegExp} text - The text to create the Locator from. 67 | * @param {GetByTextOptions} options - Optional parameters for the Locator. 68 | * @returns {Locator} - The created Locator object. 69 | */ 70 | export function getLocatorByText(text: string | RegExp, options?: GetByTextOptions): Locator { 71 | return getPage().getByText(text, options); 72 | } 73 | 74 | /** 75 | * Returns a Locator object with a specific role. 76 | * @param {GetByRoleTypes} role - The role to create the Locator from. 77 | * @param {GetByRoleOptions} options - Optional parameters for the Locator. 78 | * @returns {Locator} - The created Locator object. 79 | */ 80 | export function getLocatorByRole(role: GetByRoleTypes, options?: GetByRoleOptions): Locator { 81 | return getPage().getByRole(role, options); 82 | } 83 | 84 | /** 85 | * Returns a Locator object with a specific label. 86 | * @param {string | RegExp} text - The label text to create the Locator from. 87 | * @param {GetByRoleOptions} options - Optional parameters for the Locator. 88 | * @returns {Locator} - The created Locator object. 89 | */ 90 | export function getLocatorByLabel(text: string | RegExp, options?: GetByRoleOptions): Locator { 91 | return getPage().getByLabel(text, options); 92 | } 93 | 94 | /** 95 | * Returns a Locator object with a specific placeholder. 96 | * @param {string | RegExp} text - The place holder text to create the Locator from. 97 | * @param {GetByPlaceholderOptions} options - Optional parameters for the Locator. 98 | * @returns {Locator} - The created Locator object. 99 | */ 100 | export function getLocatorByPlaceholder(text: string | RegExp, options?: GetByPlaceholderOptions): Locator { 101 | return getPage().getByPlaceholder(text, options); 102 | } 103 | 104 | /** 105 | * Returns all Locator objects based on the input provided. 106 | * @param {string | Locator} input - The input to create the Locators from. 107 | * @param {LocatorOptions} options - Optional parameters for the Locators. 108 | * @returns {Promise} - The created Locator objects. 109 | */ 110 | export async function getAllLocators( 111 | input: string | Locator, 112 | options?: LocatorOptions & LocatorWaitOptions, 113 | ): Promise { 114 | await waitForFirstElementToBeAttached(input, options); 115 | return typeof input === 'string' ? await getPage().locator(input, options).all() : await input.all(); 116 | } 117 | 118 | /** 119 | * 2. Frames: This section contains functions and definitions related to frames. 120 | * Frames are used to handle and interact with iframes or frames within the web page. 121 | */ 122 | 123 | /** 124 | * Returns a Frame object based on the provided frame selector options. If the frame is not found, an error is thrown unless the 'force' option is set to true. 125 | * @param {FrameOptions} frameSelector - The options to identify the frame. 126 | * @param {{ force?: boolean }} options - Additional options for frame retrieval. 127 | * - force (boolean): If true, returns the frame (or null) without throwing an error if the frame is not found. 128 | * @returns {null | Frame} - The Frame object if found, otherwise null (if 'force' is true). 129 | * @throws {Error} - Throws an error if the frame is not found and 'force' is false. 130 | */ 131 | export function getFrame(frameSelector: FrameOptions, options = { force: false }): null | Frame { 132 | const frame = getPage().frame(frameSelector); 133 | if (options.force) return frame; 134 | if (!frame) { 135 | throw new Error(`Frame not found with selector: ${JSON.stringify(frameSelector)}`); 136 | } 137 | return frame; 138 | } 139 | 140 | /** 141 | * Returns a FrameLocator object based on the input provided. 142 | * @param {string | FrameLocator} frameInput - The input to create the FrameLocator from. 143 | * @returns {FrameLocator} - The created FrameLocator object. 144 | */ 145 | export function getFrameLocator(frameInput: string | FrameLocator): FrameLocator { 146 | return typeof frameInput === 'string' ? getPage().frameLocator(frameInput) : frameInput; 147 | } 148 | 149 | /** 150 | * Returns a Locator object within a specific frame based on the input provided. 151 | * @param {string | FrameLocator} frameInput - The input to create the FrameLocator from. 152 | * @param {string | Locator} input - The input to create the Locator from, within the frame. 153 | * @returns {Locator} - The created Locator object. 154 | */ 155 | export function getLocatorInFrame(frameInput: string | FrameLocator, input: string | Locator): Locator { 156 | return getFrameLocator(frameInput).locator(input); 157 | } 158 | -------------------------------------------------------------------------------- /src/vasu-playwright-lib/utils/page-utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * page-factory.ts: This module is responsible for setting and managing instances of pages. 3 | * It provides a centralized way to set and access pages, ensuring that each test has a clean, isolated page instance. 4 | * This helps to maintain the state and context of each test independently, improving test reliability and debugging. 5 | * It also includes functions for switching between pages, closing pages, and reverting to the default page. 6 | */ 7 | 8 | import { SMALL_TIMEOUT } from '../constants/timeouts'; 9 | import { BrowserContext, Page, Response, expect } from '@playwright/test'; 10 | import { 11 | GotoOptions, 12 | NavigationOptions, 13 | SwitchPageOptions, 14 | WaitForLoadStateOptions, 15 | } from '../types/optional-parameter-types'; 16 | import { getDefaultLoadState } from '../constants'; 17 | 18 | let page: Page; 19 | 20 | /** 21 | * Returns the current Page. 22 | * @returns {Page} The current Page. 23 | */ 24 | export function getPage(): Page { 25 | return page; 26 | } 27 | 28 | export function getContext(): BrowserContext { 29 | return page.context(); 30 | } 31 | 32 | /** 33 | * Sets the current Page. 34 | * @param {Page} pageInstance - The Page instance to set as the current Page. 35 | */ 36 | export function setPage(pageInstance: Page): void { 37 | page = pageInstance; 38 | } 39 | 40 | /** 41 | * Returns an array of all pages within the current context. 42 | * @returns {Page[]} An array of Page objects. 43 | */ 44 | export function getAllPages(): Page[] { 45 | return page.context().pages(); 46 | } 47 | 48 | /** 49 | * Switches to a different page by its index (1-based). 50 | * If the desired page isn't immediately available, this function will wait and retry for up to 'SMALL_TIMEOUT' seconds. 51 | * @param {number} winNum - The index of the page to switch to. 52 | * @throws {Error} If the desired page isn't found within 'SMALL_TIMEOUT' seconds. 53 | */ 54 | export async function switchPage(winNum: number, options?: SwitchPageOptions): Promise { 55 | const startTime = Date.now(); 56 | const timeoutInMs = options?.timeout || SMALL_TIMEOUT; 57 | // Wait until the desired page number exists or timeout is reached 58 | while (getAllPages().length < winNum && Date.now() - startTime < timeoutInMs) { 59 | await new Promise(resolve => setTimeout(resolve, 100)); 60 | } 61 | 62 | // Assert that the desired page number exists 63 | expect(getAllPages().length, `Page number ${winNum} not found after ${timeoutInMs} seconds`).toBeGreaterThanOrEqual( 64 | winNum, 65 | ); 66 | 67 | // Switch to the desired page and wait for it to load 68 | const pageInstance = getAllPages()[winNum - 1]; 69 | await pageInstance.waitForLoadState(options?.loadState || 'load'); 70 | setPage(pageInstance); 71 | } 72 | 73 | /** 74 | * Switches back to the default page (the first one). 75 | */ 76 | export async function switchToDefaultPage(): Promise { 77 | const allPages = getAllPages(); 78 | const noOfWindows = allPages.length; 79 | if (noOfWindows > 0) { 80 | const pageInstance = allPages[0]; 81 | await pageInstance.bringToFront(); 82 | setPage(pageInstance); 83 | } 84 | } 85 | 86 | /** 87 | * Closes a page by its index (1-based). 88 | * If no index is provided, the current page is closed. 89 | * If there are other pages open, it will switch back to the default page (Intial Page 1) if available. 90 | * @param {number} winNum - The index of the page to close. 91 | */ 92 | export async function closePage(winNum?: number): Promise { 93 | if (!winNum) { 94 | await page.close(); 95 | await switchToDefaultPage(); 96 | return; 97 | } 98 | expect(winNum, 'Window number should be Valid').toBeGreaterThan(0); 99 | const allPages = getAllPages(); 100 | const noOfWindows = allPages.length; 101 | if (noOfWindows >= 1) { 102 | const pageInstance = allPages[winNum - 1]; 103 | await pageInstance.close(); 104 | } 105 | await switchToDefaultPage(); 106 | } 107 | 108 | /** 109 | * 1. Navigations: This section contains functions for navigating within a web page or between web pages. 110 | * These functions include going to a URL, waiting for a page to load, reloading a page, and going back to a previous page. 111 | */ 112 | 113 | /** 114 | * Navigates to the specified URL. 115 | * @param {string} path - The URL to navigate to. 116 | * @param {GotoOptions} options - The navigation options. 117 | * @returns {Promise} - The navigation response or null if no response. 118 | */ 119 | export async function gotoURL( 120 | path: string, 121 | options: GotoOptions = { waitUntil: getDefaultLoadState() }, 122 | ): Promise { 123 | return await getPage().goto(path, options); 124 | } 125 | 126 | /** 127 | * Returns the URL of the page. 128 | * @param {NavigationOptions} [options] - Optional navigation options. 129 | * @returns {Promise} - The URL of the page. 130 | */ 131 | export async function getURL(options: NavigationOptions = { waitUntil: 'load' }): Promise { 132 | try { 133 | await waitForPageLoadState(options); 134 | return getPage().url(); 135 | } catch (error) { 136 | console.log(`getURL- ${error instanceof Error ? error.message : String(error)}`); 137 | return ''; 138 | } 139 | } 140 | 141 | /** 142 | * Waits for a specific page load state. 143 | * @param {NavigationOptions} options - The navigation options. 144 | */ 145 | export async function waitForPageLoadState(options?: NavigationOptions): Promise { 146 | let waitUntil: WaitForLoadStateOptions = getDefaultLoadState(); 147 | 148 | if (options?.waitUntil && options.waitUntil !== 'commit') { 149 | waitUntil = options.waitUntil; 150 | } 151 | 152 | await getPage().waitForLoadState(waitUntil); 153 | } 154 | 155 | /** 156 | * Reloads the current page. 157 | * @param {NavigationOptions} options - The navigation options. 158 | */ 159 | export async function reloadPage(options?: NavigationOptions): Promise { 160 | await Promise.all([getPage().reload(options), getPage().waitForEvent('framenavigated')]); 161 | await waitForPageLoadState(options); 162 | } 163 | 164 | /** 165 | * Navigates back to the previous page. 166 | * @param {NavigationOptions} options - The navigation options. 167 | */ 168 | export async function goBack(options?: NavigationOptions): Promise { 169 | await Promise.all([getPage().goBack(options), getPage().waitForEvent('framenavigated')]); 170 | await waitForPageLoadState(options); 171 | } 172 | 173 | /** 174 | * Waits for a specified amount of time. 175 | * @param {number} ms - The amount of time to wait in milliseconds. 176 | */ 177 | export async function wait(ms: number): Promise { 178 | // eslint-disable-next-line playwright/no-wait-for-timeout 179 | await getPage().waitForTimeout(ms); 180 | } 181 | 182 | /** 183 | * Retrieves the size of the browser window. This function uses the `evaluate` method to execute code in the context of the page, 184 | * allowing it to access the `window` object and retrieve the current window dimensions. 185 | * @returns A promise that resolves to an object containing the width and height of the window. 186 | */ 187 | export async function getWindowSize(): Promise<{ width: number; height: number }> { 188 | return await getPage().evaluate(() => { 189 | return { 190 | width: window.innerWidth, 191 | height: window.innerHeight, 192 | }; 193 | }); 194 | } 195 | 196 | /** 197 | * Saves the storage state of the current page. 198 | * 199 | * This function captures the storage state of the page, which includes cookies, 200 | * local storage, and session storage. The state can be saved to a file if a path is provided. 201 | * 202 | * @param {string} [path] - The optional file path where the storage state will be saved. 203 | * If not provided, the state will only be returned but not saved to a file. 204 | * 205 | * @returns {Promise>} - A promise that resolves to the storage state. 206 | * 207 | * @example 208 | * 209 | * // Save storage state to a file 210 | * saveStorageState('./state.json'); 211 | * 212 | * // Get storage state without saving to a file 213 | * const state = await saveStorageState(); 214 | * 215 | * @see {@link https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state | Playwright BrowserContext.storageState} 216 | */ 217 | export async function saveStorageState(path?: string): Promise> { 218 | return await getPage().context().storageState({ path: path }); 219 | } 220 | -------------------------------------------------------------------------------- /test-setup/global-setup.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * global-setup.ts: This module is responsible for setting up the global state before all tests start. 3 | * It includes a default export function that runs before all tests, setting up any necessary global context. 4 | * By centralizing these setup operations, it ensures a consistent starting point for all tests, improving test reliability. 5 | * You can add any initialization setup code within this function. 6 | */ 7 | 8 | /* import { MAX_TIMEOUT } from 'vasu-playwright-utils'; 9 | import { LOCAL_HOST_URL } from '../playwright.config'; 10 | import axios from 'axios'; 11 | import https from 'https'; 12 | 13 | const httpsAgent = new https.Agent({ 14 | rejectUnauthorized: false, // Ignore self-signed certificate error 15 | }); 16 | 17 | export default async () => { 18 | console.log('Global-setup Waiting for dev Server to load Completely'); 19 | const startTime = Date.now(); 20 | while (Date.now() - startTime < MAX_TIMEOUT) { 21 | try { 22 | // Update the URL to match your dev server URL 23 | await axios.get(LOCAL_HOST_URL, { httpsAgent }); 24 | console.log('Server is reachable and fully loaded'); 25 | break; 26 | } catch (err) { 27 | console.log('Server is unreachable ' + String(err)); // Ignore error and try again 28 | } 29 | await new Promise(resolve => setTimeout(resolve, 1000)); // Wait before trying again 30 | } 31 | console.log('dev Server setup is completed in ' + (Date.now() - startTime) + ' ms'); 32 | }; */ 33 | 34 | export default () => { 35 | // console.log("Add any initialization setup here"); 36 | }; 37 | -------------------------------------------------------------------------------- /test-setup/global-teardown.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * global-teardown.ts: This module is responsible for tearing down the global state after all tests have completed. 3 | * It includes a default export function that runs after all tests, cleaning up any necessary global context. 4 | * By centralizing these teardown operations, it ensures a consistent end state for all tests, improving test reliability. 5 | * You can add any teardown setup code within this function. 6 | * Note: Due to a known issue (https://github.com/microsoft/playwright/issues/23875), 7 | * the last line of output from this function may be cleared by the line reporter. 8 | */ 9 | 10 | export default () => { 11 | // console.log("Add any Teardown setup here"); 12 | // console.log("Add any Teardown setup here2"); 13 | }; 14 | -------------------------------------------------------------------------------- /test-setup/page-setup.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Page-setup.ts: This module is responsible for setting up the initial state of a page before each test. It includes a 3 | * hook that runs before each test, setting the page context. By centralizing these setup operations, it ensures a 4 | * consistent starting point for each test, improving test reliability. It also exports a base test object with a 5 | * beforeEach hook already set up. This can be used to define tests with the page context set up. 6 | */ 7 | 8 | import { test as baseTest, expect } from '@playwright/test'; 9 | import { setPage } from '@utils/page-utils'; 10 | 11 | /** 12 | * A hook that runs before each test, setting the page context. The base test object with a beforeEach hook is already 13 | * set up. This can be used to define tests with the page context set up. 14 | * 15 | * @param {Page} page - The page context provided by Playwright. 16 | */ 17 | export const test = baseTest.extend<{ testHook: void }>({ 18 | testHook: [ 19 | async ({ page }, use) => { 20 | // console.log('BEFORE EACH HOOK FROM FIXTURE'); 21 | setPage(page); 22 | await use(); 23 | // console.log('AFTER EACH HOOK FROM FIXTURE'); 24 | }, 25 | { auto: true }, 26 | ], 27 | }); 28 | 29 | export { expect }; 30 | -------------------------------------------------------------------------------- /tests/pages/sauce-demo/sauce-demo-login-page.ts: -------------------------------------------------------------------------------- 1 | import { click, clickAndNavigate, fill } from '@utils/action-utils'; 2 | import { failureLoginCredentials, sauceDemoCredentials } from '@testdata/sauce-demo-test-data'; 3 | import { expectElementToBeAttached, expectElementToBeVisible } from '@utils/assert-utils'; 4 | import { getLocator, getLocatorByPlaceholder, getLocatorByRole } from '@utils/locator-utils'; 5 | import { gotoURL } from '@utils/page-utils'; 6 | 7 | const userName = `#user-name`; 8 | const password = () => getLocator(`#password`).or(getLocatorByPlaceholder('Password', { exact: true })); 9 | const loginButton = () => getLocatorByRole('button', { name: 'Login' }); 10 | const logoutLink = `#logout_sidebar_link`; 11 | const errorMessage = `//*[contains(@class,'error-message')]`; 12 | 13 | export async function navigateToSauceDemoLoginPage() { 14 | await gotoURL('https://www.saucedemo.com/'); 15 | } 16 | 17 | export async function loginWithValidCredentials(validCredentials = sauceDemoCredentials) { 18 | await fill(userName, validCredentials.username); 19 | await fill(password(), validCredentials.password); 20 | await clickAndNavigate(loginButton()); 21 | await expectElementToBeAttached(logoutLink, 'User should be Logged in sucessfully'); 22 | } 23 | 24 | export async function loginWithInvalidCredentials(invalidCredentials = failureLoginCredentials) { 25 | await fill(userName, invalidCredentials.username); 26 | await fill(password(), invalidCredentials.password); 27 | await click(loginButton()); 28 | await expectElementToBeVisible(errorMessage, 'Error message should be displayed as credentials are invalid'); 29 | } 30 | 31 | export async function verifyLoginPageisDisplayed() { 32 | await expectElementToBeVisible(userName, 'Login page should be displayed'); 33 | } 34 | -------------------------------------------------------------------------------- /tests/pages/sauce-demo/sauce-demo-mini-cart.ts: -------------------------------------------------------------------------------- 1 | import { expectElementToHaveText } from '@utils/assert-utils'; 2 | import { getLocator } from '@utils/locator-utils'; 3 | 4 | const miniCartCount = () => getLocator(`//*[@id='shopping_cart_container']//span`); 5 | 6 | export async function verifyMiniCartCount(expMiniCartCount: string) { 7 | await expectElementToHaveText(miniCartCount(), expMiniCartCount); 8 | } 9 | -------------------------------------------------------------------------------- /tests/pages/sauce-demo/sauce-demo-products-page.ts: -------------------------------------------------------------------------------- 1 | import { click } from '@utils/action-utils'; 2 | import { expectElementToBeHidden, expectElementToBeVisible } from '@utils/assert-utils'; 3 | import { getLocator } from '@utils/locator-utils'; 4 | 5 | const productsContainer = () => getLocator(`#inventory_container`).nth(0); 6 | // Defining a dynamic xpath based on the position of the Add to cart element 7 | const addToCartButton = (num: number) => `(//*[@class='inventory_item'])[${num}]//*[contains(@id,'add-to-cart')]`; 8 | 9 | export async function verifyProductsPageIsDisplayed() { 10 | await expectElementToBeVisible(productsContainer(), { timeout: 1000, message: 'Logged in user should see Products' }); 11 | } 12 | 13 | export async function verifyProductsPageIsNotDisplayed() { 14 | await expectElementToBeHidden(productsContainer(), 'Products should not be displayed'); 15 | } 16 | 17 | export async function addToCartByProductNumber(productNo: number) { 18 | await click(addToCartButton(productNo)); 19 | } 20 | -------------------------------------------------------------------------------- /tests/pages/storage-setup/login-storage.setup.ts: -------------------------------------------------------------------------------- 1 | // Add the tests to store the login storage states 2 | /* import { test as setup } from '@pagesetup'; 3 | import { saveStorageState, waitForPageLoadState } from 'vasu-playwright-utils'; 4 | import * as fs from 'fs'; 5 | import { STORAGE_STATE_LOGIN } from '../../../playwright.config'; 6 | 7 | console.log('test setup file for storage state'); 8 | setup('Save Addin Storage', async () => { 9 | console.log('Starting checks for Excel Add-in Storage'); 10 | setup.skip(fs.existsSync(STORAGE_STATE_LOGIN), 'Skipping saving storage state for Add-in'); 11 | console.log('Saving Excel Add-in Storage'); 12 | // Add the login code here that will save the login storage state 13 | await waitForPageLoadState(); 14 | await saveStorageState(STORAGE_STATE_LOGIN); 15 | }); 16 | */ 17 | -------------------------------------------------------------------------------- /tests/specs/sauce-demo-all-pass.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@pagesetup'; 2 | import * as LoginPage from '@pages/sauce-demo/sauce-demo-login-page'; 3 | import * as MiniCart from '@pages/sauce-demo/sauce-demo-mini-cart'; 4 | import * as ProductsPage from '@pages/sauce-demo/sauce-demo-products-page'; 5 | 6 | /* 7 | To run the tests in parallel, you can utilize the test.describe.configure() method to set the mode to 'parallel'. 8 | By default, the tests will run sequentially when fullyParallel: false is set in playwright.config. 9 | The tests will not be skipped upon encountering a failure expect when the mode is set to 'serial'. 10 | */ 11 | test.describe.configure({ mode: 'parallel' }); 12 | 13 | test.describe('Saucedemo tests for successful, unsuccessful logins and add products to cart @smoke', () => { 14 | // beforEach hook to navigate to home page in each test 15 | test.beforeEach('Navigating to sauce demo page', async () => { 16 | await LoginPage.navigateToSauceDemoLoginPage(); 17 | }); 18 | 19 | test('Saucedemo tests - Successful login will display Products Page', async () => { 20 | await LoginPage.loginWithValidCredentials(); 21 | // verifying products page is displayed on successful login 22 | await ProductsPage.verifyProductsPageIsDisplayed(); 23 | }); 24 | 25 | test('Saucedemo test - Error message is displayed and Products page is not displayed on failed login', async () => { 26 | await LoginPage.loginWithInvalidCredentials(); 27 | // verifying Login is still displayed 28 | await LoginPage.verifyLoginPageisDisplayed(); 29 | // verifying Products Page is not displayed 30 | await ProductsPage.verifyProductsPageIsNotDisplayed(); 31 | }); 32 | 33 | test('Saucedemo test - Add product to cart', async () => { 34 | await LoginPage.loginWithValidCredentials(); 35 | await ProductsPage.verifyProductsPageIsDisplayed(); 36 | await ProductsPage.addToCartByProductNumber(1); 37 | // verifying mini cart count is updated to 1 38 | await MiniCart.verifyMiniCartCount('1'); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /tests/specs/sauce-demo-failure.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@pagesetup'; 2 | import * as LoginPage from '@pages/sauce-demo/sauce-demo-login-page'; 3 | import * as ProductsPage from '@pages/sauce-demo/sauce-demo-products-page'; 4 | 5 | /* 6 | To run the tests sequentially and skip the remaining tests in the spec file upon encountering the first failure, 7 | you can use the test.describe.configure() method to set the mode to 'serial'. 8 | By default, the tests will run sequentially if fullyParallel: false is set in `playwright.config`, and the tests will not be skipped upon failure. 9 | */ 10 | test.describe.configure({ mode: 'serial' }); 11 | 12 | test.describe('Saucedemo tests failure and skip cases', () => { 13 | // beforEach hook to navigate to home page in each test 14 | test.beforeEach('Navigating to sauce demo page', async () => { 15 | await LoginPage.navigateToSauceDemoLoginPage(); 16 | }); 17 | 18 | // This test is expected to fail due to incorrect login credentials. Review the report to analyze the failure details. 19 | test('Saucedemo tests - Failure test @fail', async () => { 20 | // This test is expected to fail so this line is added to mark the test to pass. If this line is removed, the test will fail as expected. 21 | test.fail(); 22 | await LoginPage.loginWithInvalidCredentials(); 23 | // verifying products page is displayed only on successful login 24 | await ProductsPage.verifyProductsPageIsDisplayed(); 25 | }); 26 | 27 | // This test will be skipped because the mode is set to 'serial' and the preceding test is expected to fail. 28 | test('Saucedemo tests - Successful test that will be skipped due to previous test failure', async () => { 29 | await LoginPage.loginWithValidCredentials(); 30 | // verifying products page is displayed only on successful login 31 | await ProductsPage.verifyProductsPageIsDisplayed(); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /tests/specs/sauce-demo-parameterised.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@pagesetup'; 2 | import * as LoginPage from '@pages/sauce-demo/sauce-demo-login-page'; 3 | 4 | const InvalidCredentialsData: { username: string; password: string }[] = [ 5 | { 6 | username: 'abc', 7 | password: 'abc', 8 | }, 9 | { 10 | username: '1234', 11 | password: '1234', 12 | }, 13 | ]; 14 | 15 | test.describe('Parameterising tests', () => { 16 | InvalidCredentialsData.forEach(data => { 17 | test(`Invalid Login - Running same test with different invalid data ${data.username}`, async () => { 18 | await LoginPage.navigateToSauceDemoLoginPage(); 19 | await LoginPage.loginWithInvalidCredentials(data); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/testdata/sauce-demo-test-data.ts: -------------------------------------------------------------------------------- 1 | export const sauceDemoCredentials = { 2 | username: 'standard_user', 3 | password: 'secret_sauce', 4 | }; 5 | 6 | export const failureLoginCredentials = { 7 | username: 'standard_user', 8 | password: 'invalid_password', 9 | }; 10 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts"], 4 | "compilerOptions": { 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["node_modules", "test", "test-setup", "playwright.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Language and Environment 4 | "target": "es6", 5 | 6 | // Modules 7 | "module": "commonjs", 8 | "baseUrl": ".", 9 | "moduleResolution": "node", 10 | "resolveJsonModule": true, 11 | "paths": { 12 | "@pages/*": ["./tests/pages/*"], 13 | "@testdata/*": ["./tests/testdata/*"], 14 | "@pagesetup": ["./test-setup/page-setup"], 15 | "@constants/*": ["./src/vasu-playwright-lib/constants/*"], 16 | "@utils/*": ["src/vasu-playwright-lib/utils/*"] 17 | }, // Sets up custom path mappings, enabling to import modules with convenient shorter paths 18 | 19 | // Emit 20 | "outDir": "dist", 21 | "rootDir": ".", 22 | "sourceMap": true, 23 | "declaration": true, // Generates declaration files (.d.ts) 24 | "declarationMap": true, // Generates a sourcemap for each corresponding '.d.ts' file for easy debugging using the original source code 25 | "importHelpers": true, 26 | 27 | // Interop Constraints 28 | "esModuleInterop": true, 29 | "forceConsistentCasingInFileNames": true, 30 | 31 | // Type Checking 32 | "strict": true, 33 | "strictNullChecks": true, 34 | "noUnusedLocals": true, 35 | "noUnusedParameters": true, 36 | "noFallthroughCasesInSwitch": true, 37 | "strictBindCallApply": true, 38 | "strictFunctionTypes": true, 39 | "strictPropertyInitialization": true, 40 | "noImplicitOverride": true, // Ensures that overriding methods in a subclass are marked with the 'override' keyword 41 | "noImplicitReturns": true, // Ensures that all code paths in a function explicitly return a value 42 | "typeRoots": ["node_modules/@types"], // Specifies the location of type declaration files 43 | 44 | // Completeness 45 | "skipLibCheck": true // Skip type checking of all declaration files (*.d.ts) for faster compilation times 46 | }, 47 | "include": ["src/**/*.ts", "tests/**/*.ts", "test-setup/**/*.ts", "playwright.config.ts", ".eslintrc.js"], 48 | "exclude": ["node_modules"] 49 | } 50 | --------------------------------------------------------------------------------