├── .github └── workflows │ ├── 01_ui_tests_chrome.yml │ ├── 02_ui_tests_select_one.yml │ └── 03_ui_tests_ALL.yml ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── pages ├── BasePage.ts ├── CartPage.ts ├── Components.ts ├── LoginPage.ts └── ProductsPage.ts ├── playwright.config.ts ├── test-data └── login_credentials.json ├── tests-saucedemo ├── components.spec.ts └── login.spec.ts └── utils ├── VerificationUtils.ts └── WaitUtils.ts /.github/workflows/01_ui_tests_chrome.yml: -------------------------------------------------------------------------------- 1 | # This GitHub Actions workflow is designed to execute Playwright tests using Chrome on various events 2 | # such as manual trigger, push to the main/master branch, or pull requests to the main/master branch. 3 | 4 | name: 01_Pre-defined browser Chrome 5 | 6 | on: 7 | # Manual Trigger with pre-defined browser Chrome 8 | workflow_dispatch: 9 | 10 | # Trigger on push to the main or master branch 11 | push: 12 | branches: [ main, master ] 13 | 14 | # Trigger on pull request to the main or master branch 15 | pull_request: 16 | branches: [ main, master ] 17 | 18 | jobs: 19 | # Job for running Playwright tests 20 | ui_tests_chrome: 21 | 22 | # Set a maximum timeout for the job 23 | timeout-minutes: 60 24 | 25 | # Define the machine on which tests will execute 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | # Step 1: Checking out the code from the GitHub repository to the machine (ubuntu-latest) 30 | - name: Checkout code 31 | uses: actions/checkout@v3 32 | 33 | # Step 2: Installing Node.js 34 | - name: Install Node.js 35 | uses: actions/setup-node@v3 36 | with: 37 | node-version: 20 38 | 39 | # Step 3: Install project dependencies 40 | - name: Install dependencies 41 | run: npm ci 42 | 43 | # Step 4: Install Playwright browsers and dependencies 44 | - name: Install Playwright Browsers 45 | run: npx playwright install --with-deps 46 | 47 | # Step 5: Run Playwright UI tests on Chrome browser 48 | - name: Run Playwright UI tests on Chrome browser 49 | run: npm run tests:CHROME:HEADLESS 50 | 51 | # Step 6: Upload Playwright test report as an artifact 52 | - name: Upload Reports 53 | uses: actions/upload-artifact@v4 54 | if: always() 55 | with: 56 | name: UI Tests Report 57 | path: playwright-report/ 58 | retention-days: 30 -------------------------------------------------------------------------------- /.github/workflows/02_ui_tests_select_one.yml: -------------------------------------------------------------------------------- 1 | # This GitHub Actions workflow is designed to execute Playwright tests with the option 2 | # to select the browser dynamically at run-time. 3 | # It supports manual triggers and handles inputs for browser selection. 4 | 5 | name: 02_Select Browser at run-time 6 | 7 | on: 8 | # Manual Trigger with browser selection input 9 | workflow_dispatch: 10 | inputs: 11 | browser: 12 | description: 'Select browser (CHROME | EDGE | WEBKIT | CHROMIUM | FIREFOX)' 13 | options: 14 | - 'CHROME' 15 | - 'EDGE' 16 | - 'WEBKIT' 17 | - CHROMIUM 18 | - 'FIREFOX' 19 | required: true 20 | default: 'CHROME' 21 | type: choice 22 | 23 | jobs: 24 | 25 | # Job for running Playwright tests 26 | ui_tests_browser_selected: 27 | 28 | # Set a maximum timeout for the job 29 | timeout-minutes: 60 30 | 31 | # Define the machine on which tests will execute 32 | runs-on: ubuntu-latest 33 | 34 | steps: 35 | # Step 1: Checking out the code from Github repository to the machine (ubuntu-latest) 36 | - name: Checkout code 37 | uses: actions/checkout@v3 38 | 39 | # Step 2: Installing Node JS 40 | - name: Install Node.js 41 | uses: actions/setup-node@v3 42 | with: 43 | node-version: 20 44 | 45 | # Step 3: Install project dependencies 46 | - name: Install dependencies 47 | run: npm ci 48 | 49 | # Step 4: Install Playwright browsers and dependencies 50 | - name: Install Playwright Browsers 51 | run: npx playwright install --with-deps 52 | 53 | # Step 5: Run Playwright tests on selected browser 54 | - name: Run Playwright UI tests on selected browser 55 | run: npm run tests:${{ github.event.inputs.browser }}:HEADLESS 56 | 57 | # Step 6: Upload Playwright test report as an artifact 58 | - name: Upload Reports 59 | uses: actions/upload-artifact@v4 60 | if: always() 61 | with: 62 | name: UI Tests Report 63 | path: playwright-report/ 64 | retention-days: 30 -------------------------------------------------------------------------------- /.github/workflows/03_ui_tests_ALL.yml: -------------------------------------------------------------------------------- 1 | # This GitHub Actions workflow is designed to execute Playwright tests across all supported browsers. 2 | # It supports manual triggers and utilizes a matrix strategy to run tests on 3 | # CHROME, FIREFOX, WEBKIT, CHROMIUM and EDGE browsers. 4 | 5 | name: 03_All Supported Browsers 6 | 7 | on: 8 | # Manual Trigger with all supported browsers 9 | workflow_dispatch: 10 | 11 | jobs: 12 | 13 | # Job for running Playwright tests 14 | ui_tests_ALL_browsers: 15 | 16 | # Set a maximum timeout for the job 17 | timeout-minutes: 60 18 | 19 | # Define the machine on which tests will execute 20 | runs-on: ubuntu-latest 21 | 22 | # Use matrix strategy for parallel test execution on different browsers 23 | # strategy: 24 | # matrix: 25 | # browser: [CHROME, EDGE, WEBKIT, CHROMIUM, FIREFOX] 26 | 27 | steps: 28 | # Step 1: Checking out the code from the GitHub repository to the machine (ubuntu-latest) 29 | - name: Checkout code 30 | uses: actions/checkout@v3 31 | 32 | # Step 2: Installing Node.js 33 | - name: Install Node.js 34 | uses: actions/setup-node@v3 35 | with: 36 | node-version: 20 37 | 38 | # Step 3: Install project dependencies 39 | - name: Install dependencies 40 | run: npm ci 41 | 42 | # Step 4: Install Playwright browsers and dependencies 43 | - name: Install Playwright Browsers 44 | run: npx playwright install --with-deps 45 | 46 | # Step 5: Run Playwright tests on all supported browsers from the matrix 47 | - name: Run Playwright UI tests 48 | # run: npm run tests:${{ matrix.browser }}:HEADLESS 49 | run: npm run tests:ALL:HEADLESS 50 | 51 | 52 | # Step 6: Upload Playwright test report as an artifact 53 | - name: Upload Reports 54 | uses: actions/upload-artifact@v4 55 | if: always() 56 | with: 57 | name: UI Tests Report 58 | path: playwright-report/ 59 | retention-days: 30 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore node_modules - all external packages installed via npm/yarn 2 | node_modules/ 3 | 4 | # Ignore Playwright test result folders 5 | /test-results/ 6 | /playwright-report/ 7 | /blob-report/ 8 | 9 | # Ignore Playwright's internal cache 10 | /playwright/.cache/ 11 | 12 | # macOS system file - unnecessary for projects 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 17 | --- 18 | # 💻 Test Automation Framework | WEB 19 | 20 | [![Playwright](https://img.shields.io/badge/Playwright-34495E?style=for-the-badge&logo=playwright&logoColor=white)](https://playwright.dev/) 21 | [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org/) 22 | 23 | 24 | [![VS Code](https://img.shields.io/badge/VS_Code-007ACC?style=for-the-badge&logo=visual-studio-code&logoColor=white)](https://code.visualstudio.com/) 25 | [![Playwright HTML Reporter](https://img.shields.io/badge/Playwright%20HTML%20Reporter-?style=for-the-badge&logo=playwright&logoColor=white)](https://www.npmjs.com/package/playwright-html-reporter) 26 | [![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-2088FF?style=for-the-badge&logo=github-actions&logoColor=white)](https://github.com/features/actions) 27 | 28 | ## 📑 Table of Contents 29 | 30 | - [Introduction](#introduction) 31 | - [Prerequisites](#prerequisites) 32 | - [Getting Started](#getting-started) 33 | - [Running Tests](#running-tests) 34 | - [Project Structure](#project-structure) 35 | - [Configuration](#configuration) 36 | - [Continuous Integration](#continuous-integration) 37 | - [Reporting](#reporting) 38 | - [Other Projects](#other-projects) 39 | - [Technical Documents](#technical-documents) 40 | - [Contacts](#contacts) 41 | 42 | ## 📖 Introduction 43 | This repository contains a **Test Automation Framework** built with **Playwright** and **TypeScript** for testing **web applications**. 44 | 45 | 46 | 51 | 52 | ## 🛠️ Prerequisites 53 | 54 | - [![Node.js](https://img.shields.io/badge/Node.js-43853D?style=for-the-badge&logo=node.js&logoColor=white)](https://nodejs.org/) (v18.16.1 or higher recommended) 55 | - [![npm](https://img.shields.io/badge/npm-CB3837?style=for-the-badge&logo=npm&logoColor=white)](https://www.npmjs.com/) (v9.5.1 or higher recommended) 56 | 57 | ## ▶️ Getting Started 58 | 59 | 1. **Clone the repository:** 60 | 61 | ```bash 62 | git clone https://github.com/rajatt95/TestAutomationFramework_YT_Rajat_Web_Playwright_TS.git 63 | ``` 64 | 65 | 2. **Navigate to the project directory:** 66 | 67 | ```bash 68 | cd TestAutomationFramework_YT_Rajat_Web_Playwright_TS 69 | ``` 70 | 71 | 3. **Install dependencies:** 72 | 73 | ```bash 74 | npm install 75 | ``` 76 | 77 | ## 🚀 Running Tests 78 | 79 | - **Playwright UI mode:** 80 | 81 | ```bash 82 | npm run tests:ui-mode 83 | ``` 84 | - **Playwright Debug mode:** 85 | 86 | ```bash 87 | npm run tests:debug 88 | ``` 89 | - **Playwright Codegen:** 90 | 91 | ```bash 92 | npm run playwright:codegen 93 | ``` 94 | 95 | - **Execution in different browsers:** 96 | 97 | - [![Chrome](https://img.shields.io/badge/Chrome-4285F4?style=for-the-badge&logo=google-chrome&logoColor=white)](https://www.google.com/chrome/) 98 | [![Edge](https://img.shields.io/badge/Edge-0078D7?style=for-the-badge&logo=microsoft-edge&logoColor=white)](https://www.microsoft.com/edge/)[![Firefox](https://img.shields.io/badge/Firefox-FF7139?style=for-the-badge&logo=firefox&logoColor=white)](https://www.mozilla.org/firefox/) 99 | [![WebKit](https://img.shields.io/badge/WebKit-689F63?style=for-the-badge&logo=webkit&logoColor=white)](https://webkit.org/) 100 | [![Chromium](https://img.shields.io/badge/Chromium-4A90E2?style=for-the-badge&logo=chromium&logoColor=white)](https://www.chromium.org/Home) 101 | 102 | 103 | ```bash 104 | npm run tests:CHROME 105 | ``` 106 | ```bash 107 | npm run tests:EDGE 108 | ``` 109 | ```bash 110 | npm run tests:FIREFOX 111 | ``` 112 | ```bash 113 | npm run tests:WEBKIT 114 | ``` 115 | ```bash 116 | npm run tests:CHROMIUM 117 | ``` 118 | 119 | - Execution in Headless mode: 120 | ```bash 121 | npm run tests:CHROME:HEADLESS 122 | ``` 123 | 124 | ## 📁 Project Structure 125 | 126 | The tests follow a modular and maintainable structure: 127 | 128 | ``` 129 | |-- .github 130 | | |-- workflows 131 | | |-- 01_ui_tests_chrome.yml 132 | | |-- 02_ui_tests_select_one.yml 133 | | |-- 03_ui_tests_ALL.yml 134 | |-- pages 135 | | |-- BasePage.ts 136 | | |-- CartPage.ts 137 | | |-- Components.ts 138 | | |-- LoginPage.ts 139 | | |-- ProductsPage.ts 140 | |-- test-data 141 | | |-- login_credentials.json 142 | |-- tests-saucedemo 143 | | |-- components.spec.ts 144 | | |-- login.spec.ts 145 | |-- utils 146 | | |-- VerificationUtils.ts 147 | | |-- WaitUtils.ts 148 | |-- .gitignore 149 | |-- package.json 150 | |-- playwright.config.ts 151 | ``` 152 | 153 | - `pages`: Contains the Page Object Model (POM) classes representing web pages and their elements. 154 | - `playwright-report`: Contains the HTML report for tests (Logs, Screenshots, Traces and Videos are attached). 155 | - `test-data`: Contains external files (example: login credentials data) that can be used to mock data during tests. 156 | - `tests-saucedemo`: Contains the actual test files. You can organize your tests into subdirectories as needed. 157 | - `utils`: Contains the Utilities that provides methods for asserting different conditions on web elements, waits. 158 | 159 | ## ⚙️ Configuration 160 | 161 | - Modify `playwright.config.ts` for playwright configuration settings such as 162 | - `baseURL` 163 | - `testDir` 164 | - `reporter` 165 | - `fullyParallel` 166 | - `video` 167 | - `screenshot` 168 | - `trace` 169 | 170 | ## 🔄 Continuous Integration 171 | 172 | This project is configured for CI using Github Actions. Check the configurations in `.github/workflows/*.yml`. 173 | 174 | - `01_ui_tests_chrome.yml`: This workflow executes tests in Chrome browser. 175 | 176 | - `02_ui_tests_select_one.yml`: This workflow will first ask User to select the browser for tests execution. 177 | 178 | - `03_ui_tests_ALL.yml`: This workflow executes the tests in all browsers 179 | 180 | [![Chrome](https://img.shields.io/badge/Chrome-4285F4?style=for-the-badge&logo=google-chrome&logoColor=white)](https://www.google.com/chrome/) 181 | [![Edge](https://img.shields.io/badge/Edge-0078D7?style=for-the-badge&logo=microsoft-edge&logoColor=white)](https://www.microsoft.com/edge/)[![Firefox](https://img.shields.io/badge/Firefox-FF7139?style=for-the-badge&logo=firefox&logoColor=white)](https://www.mozilla.org/firefox/) 182 | [![WebKit](https://img.shields.io/badge/WebKit-689F63?style=for-the-badge&logo=webkit&logoColor=white)](https://webkit.org/) 183 | [![Chromium](https://img.shields.io/badge/Chromium-4A90E2?style=for-the-badge&logo=chromium&logoColor=white)](https://www.chromium.org/Home) 184 | 185 | ## 📊 Reporting 186 | 187 | Playwright HTML report (Logs, Screenshots, Traces and Videos are attached) is stored in the `playwright-report` directory. 188 | 189 | ## 🔭 Other Projects 190 | 191 | - [![Java](https://img.shields.io/badge/Java-007396?style=for-the-badge&logo=java&logoColor=white)](https://github.com/stars/rajatt95/lists/programming-language-java) - 192 | [![Selenium](https://img.shields.io/badge/Selenium-43B02A?style=for-the-badge&logo=selenium&logoColor=white)](https://github.com/stars/rajatt95/lists/selenium-automation-frameworks) 193 | [![Appium](https://img.shields.io/badge/Appium-40C4FF?style=for-the-badge&logo=appium&logoColor=white)](https://github.com/stars/rajatt95/lists/appium-automation-frameworks) 194 | [![Rest Assured](https://img.shields.io/badge/Rest%20Assured-5B47A5?style=for-the-badge&logo=rest-assured&logoColor=white)](https://github.com/stars/rajatt95/lists/restassured-automation-framework) 195 | 196 | - [![JavaScript](https://img.shields.io/badge/JavaScript-F7DF1E?style=for-the-badge&logo=javascript&logoColor=black)](https://github.com/stars/rajatt95/lists/programming-language-javascript) - 197 | [![Cypress](https://img.shields.io/badge/Cypress-17202C?style=for-the-badge&logo=cypress&logoColor=white)](https://github.com/stars/rajatt95/lists/cypress-automation-frameworks) 198 | [![Playwright](https://img.shields.io/badge/Playwright-34495E?style=for-the-badge&logo=playwright&logoColor=white)](https://github.com/stars/rajatt95/lists/playwright-automation-frameworks) 199 | 200 | - [![Python](https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://github.com/stars/rajatt95/lists/programming-language-python) - 201 | [![Requests](https://img.shields.io/badge/Requests-2CA5E0?style=for-the-badge&logo=python&logoColor=white)](https://github.com/stars/rajatt95/lists/requests-automation-framework) 202 | 203 | ## 📄 Technical Documents 204 | 205 | - [![Google Drive](https://img.shields.io/badge/Google%20Drive-4285F4?style=for-the-badge&logo=google-drive&logoColor=white)](https://drive.google.com/drive/folders/1tne9pZjgWvfrS0l9tVHs6k1jnQHpTLoA?usp=sharing) 206 | - [![GitHub Repository](https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white)](https://github.com/rajatt95/Documents) 207 | 208 | ## 📧 Contacts 209 | 210 | - [![Email](https://img.shields.io/badge/Email-rajatvermaa95%40gmail.com-green)](mailto:rajatvermaa95@gmail.com) 211 | - [![GitHub Profile](https://img.shields.io/badge/GitHub-Profile-blue)](https://github.com/rajatt95) 212 | - [![GitHub Page](https://img.shields.io/badge/GitHub-Page-lightgrey)](https://rajatt95.github.io/) 213 | - [![LinkedIn](https://img.shields.io/badge/LinkedIn-Profile-blue)](https://www.linkedin.com/in/rajatt95) 214 | - [![Topmate](https://img.shields.io/badge/Topmate-Profile-red)](https://topmate.io/rajatt95) 215 | - [![Telegram](https://img.shields.io/badge/Telegram-Channel-blue)](https://t.me/rajatt95) 216 | - [![Instagram](https://img.shields.io/badge/Instagram-Profile-orange)](https://www.instagram.com/rajattvermaa95/) 217 | - [![YouTube](https://img.shields.io/badge/YouTube-Channel-red)](https://www.youtube.com/@rajatt95) 218 | - [![WhatsApp Community](https://img.shields.io/badge/WhatsApp-Community-brightgreen)](https://chat.whatsapp.com/LP20xMGvxnEL88GoB58bo1) 219 | - [![WhatsApp Channel](https://img.shields.io/badge/WhatsApp-Channel-brightgreen)](https://whatsapp.com/channel/0029Va9XXMhJ93waOU5Xer3r) 220 | 221 | Feel free to reach out if you have any questions, or suggestions, or just want to chat! 222 | 223 | Thanks for visiting my GitHub profile! 😊 224 | 225 |

rajatt95

226 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testautomationframework_yt_rajat_web_playwright_ts", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "testautomationframework_yt_rajat_web_playwright_ts", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@playwright/test": "^1.52.0", 13 | "@types/node": "^22.15.2" 14 | } 15 | }, 16 | "node_modules/@playwright/test": { 17 | "version": "1.52.0", 18 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", 19 | "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", 20 | "dev": true, 21 | "dependencies": { 22 | "playwright": "1.52.0" 23 | }, 24 | "bin": { 25 | "playwright": "cli.js" 26 | }, 27 | "engines": { 28 | "node": ">=18" 29 | } 30 | }, 31 | "node_modules/@types/node": { 32 | "version": "22.15.18", 33 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz", 34 | "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==", 35 | "dev": true, 36 | "license": "MIT", 37 | "dependencies": { 38 | "undici-types": "~6.21.0" 39 | } 40 | }, 41 | "node_modules/fsevents": { 42 | "version": "2.3.2", 43 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 44 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 45 | "dev": true, 46 | "hasInstallScript": true, 47 | "optional": true, 48 | "os": [ 49 | "darwin" 50 | ], 51 | "engines": { 52 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 53 | } 54 | }, 55 | "node_modules/playwright": { 56 | "version": "1.52.0", 57 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", 58 | "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", 59 | "dev": true, 60 | "dependencies": { 61 | "playwright-core": "1.52.0" 62 | }, 63 | "bin": { 64 | "playwright": "cli.js" 65 | }, 66 | "engines": { 67 | "node": ">=18" 68 | }, 69 | "optionalDependencies": { 70 | "fsevents": "2.3.2" 71 | } 72 | }, 73 | "node_modules/playwright-core": { 74 | "version": "1.52.0", 75 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", 76 | "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", 77 | "dev": true, 78 | "bin": { 79 | "playwright-core": "cli.js" 80 | }, 81 | "engines": { 82 | "node": ">=18" 83 | } 84 | }, 85 | "node_modules/undici-types": { 86 | "version": "6.21.0", 87 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", 88 | "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 89 | "dev": true 90 | } 91 | }, 92 | "dependencies": { 93 | "@playwright/test": { 94 | "version": "1.52.0", 95 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", 96 | "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", 97 | "dev": true, 98 | "requires": { 99 | "playwright": "1.52.0" 100 | } 101 | }, 102 | "@types/node": { 103 | "version": "22.15.18", 104 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz", 105 | "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==", 106 | "dev": true, 107 | "requires": { 108 | "undici-types": "~6.21.0" 109 | } 110 | }, 111 | "fsevents": { 112 | "version": "2.3.2", 113 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 114 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 115 | "dev": true, 116 | "optional": true 117 | }, 118 | "playwright": { 119 | "version": "1.52.0", 120 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", 121 | "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", 122 | "dev": true, 123 | "requires": { 124 | "fsevents": "2.3.2", 125 | "playwright-core": "1.52.0" 126 | } 127 | }, 128 | "playwright-core": { 129 | "version": "1.52.0", 130 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", 131 | "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", 132 | "dev": true 133 | }, 134 | "undici-types": { 135 | "version": "6.21.0", 136 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", 137 | "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 138 | "dev": true 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testautomationframework_yt_rajat_web_playwright_ts", 3 | "version": "1.0.0", 4 | "description": "This project is a Test Automation Framework built using Playwright and Typescript for automated testing of web applications.", 5 | "main": "index.js", 6 | "scripts": { 7 | "playwright:codegen": "npx playwright codegen", 8 | 9 | "playwright:tests": "npx playwright test --headed", 10 | 11 | "tests:ALL": "npx playwright test --headed", 12 | "tests:WEBKIT": "npx playwright test --project=webkit --headed", 13 | "tests:CHROMIUM": "npx playwright test --project=chromium --headed", 14 | "tests:FIREFOX": "npx playwright test --project=firefox --headed", 15 | "tests:EDGE": "npx playwright test --project='Microsoft Edge' --headed", 16 | "tests:CHROME": "npx playwright test --project='Google Chrome' --headed", 17 | 18 | "tests:ALL:HEADLESS": "npx playwright test", 19 | "tests:WEBKIT:HEADLESS": "npx playwright test --project=webkit", 20 | "tests:CHROMIUM:HEADLESS": "npx playwright test --project=chromium", 21 | "tests:FIREFOX:HEADLESS": "npx playwright test --project=firefox", 22 | "tests:EDGE:HEADLESS": "npx playwright test --project='Microsoft Edge'", 23 | "tests:CHROME:HEADLESS": "npx playwright test --project='Google Chrome'", 24 | 25 | "tests:ui-mode": "npx playwright test --ui", 26 | "tests:debug-mode": "npx playwright test --debug", 27 | 28 | "tests:show:reports-html": "npx playwright show-report", 29 | "tests:CHROME:Tag:SANITY": "npx playwright test --project='Google Chrome' --grep=@sanity", 30 | "tests:CHROME:Tag:REGRESSION": "npx playwright test --project='Google Chrome' --grep=@regression" 31 | 32 | 33 | }, 34 | "keywords": [], 35 | "author": "", 36 | "license": "ISC", 37 | "devDependencies": { 38 | "@playwright/test": "^1.52.0", 39 | "@types/node": "^22.15.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pages/BasePage.ts: -------------------------------------------------------------------------------- 1 | import { Locator } from '@playwright/test'; 2 | 3 | /** 4 | * BasePage class provides common actions that can be performed on web elements. 5 | */ 6 | class BasePage { 7 | 8 | /** 9 | * Clicks on a specified web element. 10 | * @param element - The Locator of the element to click. 11 | * @param elementName - A descriptive name of the element (used for logging). 12 | */ 13 | async clickOnWebElement(element: Locator, elementName: string): Promise { 14 | console.log(`Clicking on '${elementName}'.`); 15 | await element.click(); 16 | } 17 | 18 | /** 19 | * Fills a textbox with the provided value. 20 | * @param element - The Locator of the textbox element. 21 | * @param value - The value/text to be entered into the textbox. 22 | * @param textboxName - A descriptive name of the textbox (used for logging). 23 | */ 24 | async fillTextBox(element: Locator, value: string, textboxName: string): Promise { 25 | console.log(`Filling '${value}' in '${textboxName}' textbox.`); 26 | await element.fill(value); 27 | } 28 | } 29 | 30 | /** 31 | * Exports the BasePage class as the default export of this module. 32 | * @module BasePage 33 | */ 34 | export default BasePage; -------------------------------------------------------------------------------- /pages/CartPage.ts: -------------------------------------------------------------------------------- 1 | import { Page, Locator } from '@playwright/test'; 2 | 3 | /** 4 | * CartPage class handles operations related to the Cart page of the application. 5 | */ 6 | class CartPage { 7 | 8 | // Elements 9 | private page: Page; 10 | readonly heading_your_cart: Locator; 11 | 12 | /** 13 | * Initializes the CartPage instance with page elements. 14 | * @param {Page} page - The Playwright page object. 15 | */ 16 | constructor(page: Page) { 17 | this.page = page; 18 | this.heading_your_cart = page.locator('.title'); 19 | } 20 | 21 | // Operations/Methods 22 | 23 | } 24 | 25 | /** 26 | * Exports the CartPage class as the default export of this module. 27 | * @module CartPage 28 | */ 29 | export default CartPage; -------------------------------------------------------------------------------- /pages/Components.ts: -------------------------------------------------------------------------------- 1 | import { Page, Locator } from '@playwright/test'; 2 | 3 | // Importing the BasePage class 4 | import BasePage from "./BasePage" 5 | 6 | // Creating an instance of the BasePage class 7 | const basePage = new BasePage(); 8 | 9 | /** 10 | * Components class represents common UI components 11 | * like header, footer, and side-panel elements in the application. 12 | */ 13 | class Components { 14 | 15 | readonly page: Page; 16 | readonly header_logo_swag_labs: Locator; 17 | readonly header_icon_cart: Locator; 18 | 19 | readonly footer_msg_copyright: Locator; 20 | readonly footer_link_linkedin: Locator; 21 | readonly footer_link_twitter: Locator; 22 | readonly footer_link_facebook: Locator; 23 | 24 | readonly side_panel_icon_expand: Locator; 25 | readonly side_panel_icon_cross: Locator; 26 | readonly side_panel_link_allItems: Locator; 27 | readonly side_panel_link_about: Locator; 28 | readonly side_panel_link_logout: Locator; 29 | readonly side_panel_link_resetAppState: Locator; 30 | readonly side_panel_links: Locator; 31 | 32 | // Elements 33 | 34 | /** 35 | * Initializes locators for common components on the page. 36 | * @param page - The Playwright Page instance. 37 | */ 38 | constructor(page: Page) { 39 | this.page = page; 40 | 41 | // Header 42 | this.header_logo_swag_labs = page.locator('.app_logo'); 43 | this.header_icon_cart = page.locator('#shopping_cart_container') 44 | 45 | // Footer 46 | this.footer_msg_copyright = page.locator('.footer_copy'); 47 | this.footer_link_linkedin = page.getByRole('link', { name: 'LinkedIn' }); 48 | this.footer_link_twitter = page.getByRole('link', { name: 'Twitter' }) 49 | this.footer_link_facebook = page.getByRole('link', { name: 'Facebook' }) 50 | 51 | // Side-Panel 52 | this.side_panel_icon_expand = page.locator('#react-burger-menu-btn') 53 | this.side_panel_icon_cross = page.locator('#react-burger-cross-btn') 54 | this.side_panel_link_allItems = page.locator('#inventory_sidebar_link') 55 | this.side_panel_link_about = page.locator('#about_sidebar_link') 56 | this.side_panel_link_logout = page.locator('#logout_sidebar_link') 57 | this.side_panel_link_resetAppState = page.locator('#reset_sidebar_link') 58 | this.side_panel_links = page.locator("//*[contains(@id,'sidebar_link')]") 59 | } 60 | 61 | // Operations/Methods 62 | 63 | // Header 64 | 65 | /** 66 | * Clicks the cart icon in the header. 67 | * @returns {Promise} - A promise that resolves when the cart icon has been clicked. 68 | */ 69 | async click_header_icon_cart(): Promise { 70 | const basePage = new BasePage(); 71 | await basePage.clickOnWebElement(this.header_icon_cart, "Header: Cart icon"); 72 | } 73 | 74 | // Footer 75 | 76 | // Side-Panel 77 | 78 | /** 79 | * Clicks on the expand icon in the side-panel. 80 | */ 81 | async click_side_panel_icon_expand(): Promise { 82 | await basePage.clickOnWebElement(this.side_panel_icon_expand, "Side-Panel: Expand icon"); 83 | } 84 | 85 | /** 86 | * Clicks on the cross icon in the side-panel. 87 | */ 88 | async click_side_panel_icon_cross(): Promise { 89 | await basePage.clickOnWebElement(this.side_panel_icon_cross, "Side-Panel: Cross icon"); 90 | } 91 | 92 | /** 93 | * Clicks on the about link in the side-panel. 94 | */ 95 | async click_side_panel_link_about(): Promise { 96 | await basePage.clickOnWebElement(this.side_panel_link_about, "Side-Panel: About link"); 97 | } 98 | 99 | } 100 | 101 | /** 102 | * Exports the Components class as the default export of this module. 103 | * @module Components 104 | */ 105 | export default Components; -------------------------------------------------------------------------------- /pages/LoginPage.ts: -------------------------------------------------------------------------------- 1 | import { Page, Locator } from '@playwright/test'; 2 | 3 | // Importing the BasePage class 4 | import BasePage from "./BasePage" 5 | 6 | // Creating an instance of the BasePage class 7 | const basePage = new BasePage(); 8 | /** 9 | * LoginPage class handles operations related to the login page of the application. 10 | */ 11 | class LoginPage { 12 | 13 | private page: Page; 14 | readonly textbox_username: Locator; 15 | readonly textbox_password: Locator; 16 | readonly button_login: Locator; 17 | readonly message_error_not_match: Locator; 18 | 19 | // Elements 20 | 21 | /** 22 | * Initializes locators for login page elements. 23 | * @param page - The Playwright Page instance. 24 | * 25 | * @example 26 | * const loginPage = new LoginPage(page); 27 | * // Now you can access locators like loginPage.textbox_username 28 | */ 29 | constructor(page: Page) { 30 | this.page = page; 31 | this.textbox_username = page.locator('[data-test="username"]'); 32 | this.textbox_password = page.locator('[data-test="password"]'); 33 | this.button_login = page.locator('[data-test="login-button"]'); 34 | this.message_error_not_match = page.locator('xpath=//h3[contains(text(),"do not match")]'); 35 | } 36 | 37 | // Operations/Methods 38 | 39 | /** 40 | * Logs into the application by filling in the username and password fields, 41 | * and clicking the login button. 42 | * @param username - Username to input. 43 | * @param password - Password to input. 44 | * 45 | * @example 46 | * await loginPage.loginToApplication('user', 'password'); 47 | */ 48 | async loginToApplication(username: string, password: string): Promise { 49 | const basePage = new BasePage(); 50 | await basePage.fillTextBox(this.textbox_username, username, "Username") 51 | await basePage.fillTextBox(this.textbox_password, password, "Password") 52 | await basePage.clickOnWebElement(this.button_login, "Login button") 53 | } 54 | 55 | /** 56 | * Logs into the application using predefined valid credentials. 57 | * The credentials are loaded from a JSON file. 58 | * 59 | * @example 60 | * await loginPage.loginToApplicationWithValidCredentials(); 61 | */ 62 | async loginToApplicationWithValidCredentials(): Promise { 63 | 64 | // Loading login credentials from JSON file 65 | const loginCredentials = require('../test-data/login_credentials.json'); 66 | 67 | // Extracting credentials for valid case 68 | const { valid_username, valid_password } = loginCredentials.data.credentials_1; 69 | 70 | // Call loginToApplication with valid credentials 71 | await this.loginToApplication(valid_username, valid_password); 72 | } 73 | 74 | } 75 | 76 | /** 77 | * Exports the LoginPage class as the default export of this module. 78 | * @module LoginPage 79 | */ 80 | export default LoginPage; -------------------------------------------------------------------------------- /pages/ProductsPage.ts: -------------------------------------------------------------------------------- 1 | import { Page, Locator } from '@playwright/test'; 2 | 3 | /** 4 | * ProductsPage class handles operations related to the Products page of the application. 5 | */ 6 | class ProductsPage { 7 | 8 | private page: Page; 9 | readonly heading_products: Locator; 10 | 11 | // Elements 12 | 13 | /** 14 | * Initializes locators for the Products page elements. 15 | * @param page - The Playwright Page instance. 16 | */ 17 | constructor(page: Page) { 18 | this.page = page; 19 | this.heading_products = page.locator('.title'); 20 | } 21 | 22 | // Operations/Methods 23 | 24 | } 25 | 26 | /** 27 | * Exports the ProductsPage class as the default export of this module. 28 | * @module ProductsPage 29 | */ 30 | export default ProductsPage; -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | 3 | /** 4 | * Read environment variables from file. 5 | * https://github.com/motdotla/dotenv 6 | */ 7 | // import dotenv from 'dotenv'; 8 | // import path from 'path'; 9 | // dotenv.config({ path: path.resolve(__dirname, '.env') }); 10 | 11 | /** 12 | * See https://playwright.dev/docs/test-configuration. 13 | */ 14 | export default defineConfig({ 15 | // testDir: './tests', 16 | testDir: './tests-saucedemo', 17 | 18 | timeout: 60000, // Set the timeout to 60 seconds (in milliseconds) 19 | 20 | /* Run tests in files in parallel */ 21 | fullyParallel: true, 22 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 23 | forbidOnly: !!process.env.CI, 24 | 25 | /* Retry on CI only */ 26 | // retries: process.env.CI ? 2 : 0, 27 | retries: 1, // Give failing tests 1 retry attempt(s) 28 | 29 | /* Opt out of parallel tests on CI. */ 30 | // workers: process.env.CI ? 1 : undefined, 31 | workers:6, 32 | 33 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 34 | reporter: 'html', 35 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 36 | use: { 37 | /* Base URL to use in actions like `await page.goto('/')`. */ 38 | baseURL: process.env.BASE_URL || 'https://www.saucedemo.com/', 39 | // BASE_URL=https://www-prod.saucedemo.com npx playwright test 40 | 41 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 42 | // trace: 'on-first-retry', 43 | trace: 'on', 44 | 45 | // video: 'retain-on-failure', 46 | video: 'on', 47 | 48 | // screenshot: 'only-on-failure', 49 | screenshot: 'on', 50 | }, 51 | 52 | /* Configure projects for major browsers */ 53 | projects: [ 54 | { 55 | name: 'chromium', 56 | use: { ...devices['Desktop Chrome'] }, 57 | }, 58 | 59 | { 60 | name: 'firefox', 61 | use: { ...devices['Desktop Firefox'] }, 62 | }, 63 | 64 | { 65 | name: 'webkit', 66 | use: { ...devices['Desktop Safari'] }, 67 | }, 68 | 69 | /* Test against mobile viewports. */ 70 | // { 71 | // name: 'Mobile Chrome', 72 | // use: { ...devices['Pixel 5'] }, 73 | // }, 74 | // { 75 | // name: 'Mobile Safari', 76 | // use: { ...devices['iPhone 12'] }, 77 | // }, 78 | 79 | /* Test against branded browsers. */ 80 | { 81 | name: 'Microsoft Edge', 82 | use: { ...devices['Desktop Edge'], channel: 'msedge' }, 83 | }, 84 | { 85 | name: 'Google Chrome', 86 | use: { ...devices['Desktop Chrome'], channel: 'chrome' }, 87 | }, 88 | ], 89 | 90 | /* Run your local dev server before starting the tests */ 91 | // webServer: { 92 | // command: 'npm run start', 93 | // url: 'http://127.0.0.1:3000', 94 | // reuseExistingServer: !process.env.CI, 95 | // }, 96 | }); 97 | -------------------------------------------------------------------------------- /test-data/login_credentials.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "comment_1": "Credentials - VALID", 4 | "credentials_1": { 5 | "valid_username": "standard_user", 6 | "valid_password": "secret_sauce" 7 | }, 8 | "comment_2": "Credentials - INVALID", 9 | "credentials_2": { 10 | "invalid_username": "invalid_email@gmail.com", 11 | "invalid_password": "invalid_password" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /tests-saucedemo/components.spec.ts: -------------------------------------------------------------------------------- 1 | // Importing necessary modules for Playwright test 2 | import { test } from '@playwright/test'; 3 | 4 | // Importing page objects 5 | import LoginPage from '../pages/LoginPage'; 6 | import CartPage from '../pages/CartPage'; 7 | import Components from '../pages/Components'; 8 | 9 | // Importing utilities 10 | import verificationUtils from '../utils/VerificationUtils'; 11 | import waitUtils from '../utils/WaitUtils'; 12 | 13 | /** 14 | * Test suite for Sauce Demo Application Components. 15 | */ 16 | test.describe('[COMPONENTS]', () => { 17 | 18 | /** 19 | * Before each test, navigate to the application homepage and login. 20 | */ 21 | test.beforeEach(async ({ page }) => { 22 | // Navigate to application and Login 23 | await page.goto('/'); 24 | const loginPage = new LoginPage(page); 25 | await loginPage.loginToApplicationWithValidCredentials(); 26 | }); 27 | 28 | /** 29 | * Test case: [Header] Static Messages. Validate that User is able to see messages in Header component. 30 | * @tags {regression, sanity} 31 | */ 32 | test('[Header] Static Messages. Validate that User is able to see messages in Header component. @regression @sanity', async ({ page }) => { 33 | 34 | // Verify the side-panel expand icon on the header 35 | const components = new Components(page); 36 | verificationUtils.elementIsVisible(components.side_panel_icon_expand, "Side-Panel: Expand icon"); 37 | 38 | // Verify the logo on the header 39 | verificationUtils.elementHasText(components.header_logo_swag_labs, 'Swag Labs'); 40 | 41 | // Verify the cart icon on the header 42 | verificationUtils.elementIsVisible(components.header_icon_cart, "Header: Cart icon"); 43 | 44 | // Verify the CSS Property of the logo 45 | verificationUtils.elementHasCSSPropertyAndHasValue(components.header_logo_swag_labs, "Header: Swag Labs", "font-size","24px") 46 | // verificationUtils.elementHasCSSPropertyAndHasValue(components.header_logo_swag_labs, "Header: Swag Labs", "font-family",'"DM Mono", "sans-serif"') 47 | verificationUtils.elementHasCSSPropertyAndHasValue(components.header_logo_swag_labs, "Header: Swag Labs", "color","rgb(19, 35, 34)") 48 | 49 | }); 50 | 51 | /** 52 | * Test case: [Header] Navigate to Cart Page. Validate that User is able to navigate to Cart Page using Cart icon. 53 | * @tags {regression} 54 | */ 55 | test('[Header] Navigate to Cart Page. Validate that User is able to navigate to Cart Page using Cart icon. @regression', async ({ page }) => { 56 | 57 | // Click on Cart icon 58 | const components = new Components(page); 59 | await components.click_header_icon_cart(); 60 | 61 | // Verify that User is on Cart Page 62 | const cartPage = new CartPage(page); 63 | verificationUtils.elementHasText(cartPage.heading_your_cart, 'Your Cart'); 64 | 65 | // Verify the Page URL 66 | verificationUtils.pageContainsUrl(page, 'cart'); 67 | // verificationUtils.pageHasUrl(page, 'https://www.saucedemo.com/cart.html'); 68 | verificationUtils.pageHasUrl(page, 'cart.html'); // baseUrl value will be fetched from playwright.config.ts file 69 | 70 | // Verify the Page Title 71 | // verificationUtils.pageContainsTitle(page, 'Labs'); 72 | verificationUtils.pageHasTitle(page, 'Swag Labs'); 73 | }); 74 | 75 | /** 76 | * Test case: [Footer] Static Messages. Validate that User is able to see messages in Footer component. 77 | * @tags {regression, sanity} 78 | */ 79 | test('[Footer] Static Messages. Validate that User is able to see messages in Footer component. @regression @sanity', async ({ page }) => { 80 | const components = new Components(page); 81 | 82 | // Verify the social links icons are visible 83 | verificationUtils.elementIsVisible(components.footer_link_twitter, "Footer: Twitter link"); 84 | verificationUtils.elementIsVisible(components.footer_link_facebook, "Footer: Facebook link"); 85 | verificationUtils.elementIsVisible(components.footer_link_linkedin, "Footer: LinkedIn link"); 86 | 87 | // Verify the copyright message 88 | verificationUtils.elementContainsText(components.footer_msg_copyright, 'Sauce Labs. All Rights Reserved. Terms of Service | Privacy Policy'); 89 | 90 | }); 91 | 92 | /** 93 | * Test case: [Footer] Links navigation. Validate that User is able to navigate to social platforms using icons. 94 | * @tags {regression} 95 | */ 96 | test('[Footer] Links navigation. Validate that User is able to navigate to social platforms using icons. @regression', async ({ page }) => { 97 | const components = new Components(page); 98 | 99 | // Verify the social links have correct href attributes 100 | verificationUtils.elementHasAttributeAndHasValue(components.footer_link_twitter, "Footer: Twitter link", "href", "https://twitter.com/saucelabs"); 101 | 102 | verificationUtils.elementHasAttributeAndHasValue(components.footer_link_facebook, "Footer: Facebook link", "href", "https://www.facebook.com/saucelabs"); 103 | 104 | verificationUtils.elementHasAttributeAndHasValue(components.footer_link_linkedin, "Footer: LinkedIn link", "href", "https://www.linkedin.com/company/sauce-labs/"); 105 | 106 | }); 107 | 108 | /** 109 | * Test case: [Side-Panel] Static Messages. Validate that User is able to see messages in Side-Panel component. 110 | * @tags {regression, sanity} 111 | */ 112 | test('[Side-Panel] Static Messages. Validate that User is able to see messages in Side-Panel component. @regression @sanity', async ({ page }) => { 113 | 114 | // Open Side-Panel 115 | const components = new Components(page); 116 | await components.click_side_panel_icon_expand(); 117 | 118 | // Verify Links in Side-Panel 119 | verificationUtils.elementHasText(components.side_panel_link_allItems, "All Items"); 120 | verificationUtils.elementHasText(components.side_panel_link_about, "About"); 121 | verificationUtils.elementHasText(components.side_panel_link_logout, "Logout"); 122 | verificationUtils.elementHasText(components.side_panel_link_resetAppState, "Reset App State"); 123 | 124 | verificationUtils.elementsCount(components.side_panel_links, "Side-Panel links", 4) 125 | 126 | verificationUtils.elementIsVisible(components.side_panel_icon_cross, "Side-Panel: Cross icon"); 127 | 128 | }); 129 | 130 | /** 131 | * Test case: [Side-Panel] Panel Expand/Collapse. Validate that User is able to expand/collapse panel using icons. 132 | * @tags {regression} 133 | */ 134 | test('[Side-Panel] Panel Expand/Collapse. Validate that User is able to expand/collapse panel using icons. @regression', async ({ page }) => { 135 | 136 | // Open Side-Panel 137 | const components = new Components(page); 138 | await components.click_side_panel_icon_expand(); 139 | 140 | await waitUtils.waitForGivenTime(2); // Wait 2 seconds 141 | 142 | verificationUtils.elementIsVisible(components.side_panel_icon_cross, "Side-Panel: Cross icon"); 143 | 144 | // Close Side-Panel 145 | await components.click_side_panel_icon_cross(); 146 | verificationUtils.elementIsVisible(components.side_panel_icon_expand, "Side-Panel: Expand icon"); 147 | 148 | }); 149 | 150 | /** 151 | * Test case: [Side-Panel] Link: About. Validate that User is able to navigate to official website using About link. 152 | * @tags {regression} 153 | */ 154 | test('[Side-Panel] Link: About. Validate that User is able to navigate to official website using About link. @regression', async ({ page }) => { 155 | 156 | // Open Side-Panel 157 | const components = new Components(page); 158 | await components.click_side_panel_icon_expand(); 159 | 160 | // Click About Link 161 | await components.click_side_panel_link_about(); 162 | 163 | // Verify Page URL and Title 164 | verificationUtils.pageHasTitle(page, 'Sauce Labs: Cross Browser Testing, Selenium Testing & Mobile Testing'); 165 | verificationUtils.pageHasUrl(page, 'https://saucelabs.com/'); 166 | 167 | }); 168 | 169 | 170 | }); -------------------------------------------------------------------------------- /tests-saucedemo/login.spec.ts: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { test, expect } from '@playwright/test'; 3 | 4 | // Importing page objects 5 | import LoginPage from '../pages/LoginPage'; 6 | import ProductsPage from '../pages/ProductsPage'; 7 | import Components from '../pages/Components'; 8 | import verificationUtils from '../utils/VerificationUtils'; 9 | 10 | // Importing test data 11 | import loginCredentials from '../test-data/login_credentials.json'; 12 | 13 | // Extracting credentials for both valid and invalid cases 14 | const { 15 | credentials_1: { valid_username, valid_password }, 16 | credentials_2: { invalid_username, invalid_password } 17 | } = loginCredentials.data; 18 | 19 | 20 | /** 21 | * Test suite for Sauce Demo login functionality. 22 | */ 23 | test.describe('[LOGIN]', () => { 24 | 25 | /** 26 | * Before each test, navigate to the application homepage. 27 | */ 28 | test.beforeEach(async({ page }) =>{ 29 | // Navigate to application 30 | await page.goto('/') 31 | }) 32 | 33 | /** 34 | * [LOGIN] Test Case: 35 | * Validate that a user is able to successfully log in using valid credentials. 36 | * - Verify Products page heading after login 37 | * - Verify header logo 38 | * - Verify footer elements and LinkedIn link 39 | * 40 | * Tags: @regression @sanity 41 | */ 42 | test('Login with valid credentials. Validate that User is able to login using valid credentials. @regression @sanity', async ({ page }) => { 43 | 44 | // Navigate to application 45 | await page.goto('/'); 46 | 47 | // Perform login with valid credentials 48 | const loginPage = new LoginPage(page) 49 | await loginPage.loginToApplication(valid_username, valid_password) 50 | 51 | // Assertions for successful login 52 | 53 | // Verify the heading on the Products page 54 | const productsPage = new ProductsPage(page) 55 | verificationUtils.elementHasText(productsPage.heading_products, 'Products') 56 | 57 | // Verify the logo on the header 58 | const components = new Components(page) 59 | verificationUtils.elementHasText(components.header_logo_swag_labs, 'Swag Labs') 60 | 61 | // Verify the copyright message in the footer 62 | verificationUtils.elementContainsText(components.footer_msg_copyright, ' Sauce Labs. All Rights Reserved. Terms of Service | Privacy Policy') 63 | 64 | // Verify that LinkedIn link in the footer is present 65 | verificationUtils.elementIsVisible(components.footer_link_linkedin, "Footer: LinkedIn link") 66 | 67 | // Verify the href attribute and value for the LinkedIn link in the footer 68 | verificationUtils.elementHasAttributeAndHasValue(components.footer_link_linkedin, "Footer: LinkedIn link", 'href', 'https://www.linkedin.com/company/sauce-labs/') 69 | 70 | }); 71 | 72 | /** 73 | * [LOGIN] Test Case: 74 | * Validate that a user is unable to log in using invalid credentials. 75 | * - Verify the error message for incorrect Username and Password 76 | * 77 | * Tags: @regression 78 | */ 79 | test('Login with invalid credentials. Validate that User is unable to login using invalid credentials. @regression', async ({ page }) => { 80 | 81 | // Navigate to application 82 | await page.goto('/'); 83 | 84 | // Perform login with invalid credentials 85 | const loginPage = new LoginPage(page) 86 | await loginPage.loginToApplication(invalid_username, invalid_password) 87 | 88 | // Verify the error message for Username and Password mismatch 89 | verificationUtils.elementContainsText(loginPage.message_error_not_match, 'Username and password do not match') 90 | 91 | }); 92 | 93 | }); -------------------------------------------------------------------------------- /utils/VerificationUtils.ts: -------------------------------------------------------------------------------- 1 | import { expect, Locator, Page } from '@playwright/test'; 2 | 3 | /** 4 | * Utility class for performing various verification/assertion actions on web elements. 5 | */ 6 | class VerificationUtils { 7 | 8 | /** 9 | * Asserts that the target element contains the expected text. 10 | * @param targetElement - The Locator of the element to be verified. 11 | * @param expectedText - The text expected to be contained within the element. 12 | */ 13 | async elementContainsText(targetElement: Locator, expectedText: string): Promise { 14 | console.log(`Asserts that an element contains the expected text '${expectedText}'.`) 15 | await expect(targetElement).toContainText(expectedText); 16 | } 17 | 18 | /** 19 | * Asserts that the target element's text matches the expected text exactly. 20 | * @param targetElement - The Locator of the element to be verified. 21 | * @param expectedText - The exact text expected to match. 22 | */ 23 | async elementHasText(targetElement: Locator, expectedText: string): Promise { 24 | console.log(`Asserts that an element has the expected text '${expectedText}'.`) 25 | await expect(targetElement).toHaveText(expectedText); 26 | } 27 | 28 | /** 29 | * Asserts that the specified element is visible on the page. 30 | * @param targetElement - The Locator of the element to be verified. 31 | * @param targetElementName - A descriptive name of the element for logging purposes. 32 | */ 33 | async elementIsVisible(targetElement: Locator, targetElementName: string): Promise { 34 | console.log(`Asserts that '${targetElementName}' is visible.`) 35 | await expect(targetElement).toBeVisible(); 36 | } 37 | 38 | /** 39 | * Asserts that the specified element is not visible on the page. 40 | * @param targetElement - The Locator of the element to be verified. 41 | * @param targetElementName - A descriptive name of the element for logging purposes. 42 | */ 43 | async elementIsNotVisible(targetElement: Locator, targetElementName: string): Promise { 44 | console.log(`Asserts that '${targetElementName}' is not visible.`) 45 | await expect(targetElement).toBeHidden(); 46 | } 47 | 48 | /** 49 | * Asserts that the target element has a specific attribute with the expected value. 50 | * @param targetElement - The Locator of the element to be verified. 51 | * @param targetElementName - A descriptive name of the element for logging purposes. 52 | * @param attribute - The attribute name to check. 53 | * @param attributeValue - The expected value of the attribute. 54 | */ 55 | async elementHasAttributeAndHasValue(targetElement: Locator, targetElementName: string, attribute: string, attributeValue: string): Promise { 56 | console.log(`Asserts that '${targetElementName}' has a specific attribute '${attribute}' with the expected value '${attributeValue}'.`) 57 | await expect(targetElement).toHaveAttribute(attribute, attributeValue); 58 | } 59 | 60 | /** 61 | * Asserts that the current page URL contains the expected substring. 62 | * @param page - The Playwright page object. 63 | * @param expectedUrl - The substring to check for in the page URL. 64 | */ 65 | async pageContainsUrl(page: Page, expectedUrl: string): Promise { 66 | const currentPageUrl = await page.url(); 67 | console.log(`Asserts that the current page URL '${currentPageUrl}' contains the expected substring '${expectedUrl}'.`) 68 | expect(currentPageUrl).toContain(expectedUrl); 69 | } 70 | 71 | /** 72 | * Asserts that the current page URL matches the expected URL. 73 | * @param page - The Playwright page object. 74 | * @param expectedUrl - The expected URL to match. 75 | */ 76 | async pageHasUrl(page: Page, expectedUrl: string): Promise { 77 | console.log(`Asserts that the current page URL matches the expected substring '${expectedUrl}'.`) 78 | await expect(page).toHaveURL(expectedUrl); 79 | } 80 | 81 | /** 82 | * Asserts that the current page Title contains the expected substring. 83 | * @param page - The Playwright page object. 84 | * @param expectedTitle - The substring to check for in the page title. 85 | */ 86 | async pageContainsTitle(page: Page, expectedTitle: string): Promise { 87 | const currentPageTitle = await page.title(); 88 | console.log(`Asserts that the current page Title '${currentPageTitle}' contains the expected substring '${expectedTitle}'.`) 89 | expect(currentPageTitle).toContain(expectedTitle); 90 | } 91 | 92 | /** 93 | * Asserts that the current page Title matches the expected title. 94 | * @param page - The Playwright page object. 95 | * @param expectedTitle - The expected title to match. 96 | */ 97 | async pageHasTitle(page: Page, expectedTitle: string): Promise { 98 | console.log(`Asserts that the current page Title matches the expected substring '${expectedTitle}'.`) 99 | await expect(page).toHaveTitle(expectedTitle); 100 | } 101 | 102 | /** 103 | * Asserts that the number of elements matching the locator equals the expected count. 104 | * 105 | * @param targetElement - The locator for the target elements. 106 | * @param targetElementName - A friendly name for logging purposes. 107 | * @param expectedCount - The expected number of elements. 108 | */ 109 | async elementsCount(targetElement: Locator, targetElementName: string, expectedCount: number): Promise { 110 | if (expectedCount === 1) { 111 | console.log(`Asserts that ${expectedCount} '${targetElementName}' is visible.`); 112 | } else { 113 | console.log(`Asserts that ${expectedCount} '${targetElementName}' are visible.`); 114 | } 115 | 116 | await expect(targetElement).toHaveCount(expectedCount); 117 | } 118 | 119 | /** 120 | * Asserts that an element has a specific CSS property with the expected value. 121 | * 122 | * @param {Locator} targetElement - The target element to check. 123 | * @param {string} targetElementName - The name or identifier of the target element. 124 | * @param {string} property - The CSS property to check. 125 | * @param {string} propertyValue - The expected value of the CSS property. 126 | */ 127 | async elementHasCSSPropertyAndHasValue(targetElement: Locator, targetElementName: string, property: string, propertyValue: string): Promise { 128 | console.log(`Asserts that '${targetElementName}' has a specific CSS property '${property}' with the expected value '${propertyValue}'.`) 129 | await expect(targetElement).toHaveCSS(property, propertyValue); 130 | } 131 | } 132 | 133 | 134 | /** 135 | * Exports the VerificationUtils class as the default export of this module. 136 | * @module VerificationUtils 137 | */ 138 | export default new VerificationUtils(); -------------------------------------------------------------------------------- /utils/WaitUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * WaitUtils class provides methods for introducing waits during test execution. 3 | */ 4 | class WaitUtils { 5 | 6 | /** 7 | * Waits for a given amount of time. 8 | * 9 | * @param timeInSeconds - The time to wait in seconds. 10 | */ 11 | async waitForGivenTime(timeInSeconds: number): Promise { 12 | console.log(`Waiting for '${timeInSeconds}' seconds.`); 13 | await new Promise(resolve => setTimeout(resolve, timeInSeconds * 1000)); 14 | } 15 | 16 | } 17 | 18 | /** 19 | * Exports the WaitUtils class as the default export of this module. 20 | * @module WaitUtils 21 | */ 22 | export default new WaitUtils(); --------------------------------------------------------------------------------