diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..71b9e19 Binary files /dev/null and b/.DS_Store differ diff --git a/.env b/.env new file mode 100644 index 0000000..80af16a --- /dev/null +++ b/.env @@ -0,0 +1,31 @@ +FUND_FIT_LANDING_PAGE_URL=https://streamlyne.com/fundfit/ +FUND_FIT_TEST_ENV_URL=https://test-fundfit.streamlyne.org/ +FUND_FIT_LOGIN_PAGE_URL=https://test-fundfit.streamlyne.org/login +FUND_FIT_REGISTER_PAGE_URL=https://test-fundfit.streamlyne.org/register +FUND_FIT_USER_EMAIL=mahbubasr091@gmail.com +FUND_FIT_USER_NEW_EMAIL=mahbubasr091@gmail.com +FUND_FIT_USER_PASSWORD=streamLyne8068!@ +FUND_FIT_USER_PREVIOUS_PASSWORD=streamLyne2025@ +FUND_FIT_FORGET_PASSWORD_PAGE_URL=https://test-fundfit.streamlyne.org/forgot-password +FUND_FIT_DASHBOARD_PAGE_URL=https://test-fundfit.streamlyne.org/dashboard +FUND_FIT_START_MATCHING_PAGE_URL=https://test-fundfit.streamlyne.org/matcher +FUND_FIT_MATCHES_PAGE_URL=https://test-fundfit.streamlyne.org/match +FUND_FIT_NOTIFICATIONS_PAGE_URL=https://test-fundfit.streamlyne.org/notifications + +FUND_FIT_PROFILE_PAGE_URL=https://test-fundfit.streamlyne.org/profile +FUND_FIT_PROFILE_EDIT_PAGE_URL=https://test-fundfit.streamlyne.org/profile/settings +FUND_FIT_PROFILE_PASSWORD_SETTING_PAGE_URL=https://test-fundfit.streamlyne.org/profile/settings/password +FUND_FIT_TEST_PASSWORD=test2025@FundFit +FUND_FIT_PROFILE_EMAIL_SETTING_PAGE_URL=https://test-fundfit.streamlyne.org/profile/settings/email + +FUND_FIT_PROFILE_BACKGROUND_PAGE_URL=https://test-fundfit.streamlyne.org/profile/background +FUND_FIT_PROFILE_BACKGROUND_CREATE_PAGE_URL=https://test-fundfit.streamlyne.org/profile/background/create +FUND_FIT_PROFILE_BACKGROUND_UPDATE_PAGE_URL=https://test-fundfit.streamlyne.org/profile/background/update + +FUND_FIT_PROFILE_WORK_PAGE_URL=https://test-fundfit.streamlyne.org/profile/work +FUND_FIT_PROFILE_WORK_CREATE_PAGE_URL=https://test-fundfit.streamlyne.org/profile/work/create +FUND_FIT_PROFILE_WORK_UPDATE_PAGE_URL=https://test-fundfit.streamlyne.org/profile/work/update + +FUND_FIT_PROFILE_PROJECT_PAGE_URL=https://test-fundfit.streamlyne.org/profile/funding +FUND_FIT_PROFILE_ATTACHMENT_PAGE_URL=https://test-fundfit.streamlyne.org/profile/attachment +FUND_FIT_CREATE_PROJECT_PAGE_URL=https://test-fundfit.streamlyne.org/profile/funding/create diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 18c11a1..a8e5c4b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,33 +1,23 @@ -name: Playwright Test Suite -on: - [push] - +name: Playwright Tests +on: [push, pull_request] jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - - name: Install dependencies - run: npm ci - - - name: Install Playwright Browsers - run: npx playwright install --with-deps - - - name: Run Playwright tests - run: | - npx playwright test - npm install -D allure-commandline - - name: Generate Allure report - run: npx allure generate allure-results --clean -o allure-report - - - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} - with: - name: allure-reports - path: allure-report/ - retention-days: 30 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 67e9360..76de6c3 100644 --- a/.gitignore +++ b/.gitignore @@ -73,7 +73,7 @@ web_modules/ .yarn-integrity # dotenv environment variable files -.env +# .env .env.development.local .env.test.local .env.production.local @@ -135,3 +135,4 @@ dist /blob-report/ /playwright/.cache/ /reports/* +/allure-results/* diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 17e60a4..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 Md. Mahbubur Rahman (Tom) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 0a4940e..b7d06af 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,119 @@ -# playwright_TS_Project -creating playwright hybrid framework using Typescript. +# Streamlyne FundFit Test + +
+ +An interactive test automation project using Playwright and TypeScript. + +## Overview + +This project is designed to automate testing for the Streamlyne FundFit application. It leverages the power of Playwright, a modern end-to-end testing framework, combined with TypeScript for type safety and maintainability. + +## Features + +- Cross-browser testing (Chromium, Firefox, WebKit) +- Headless and headed execution modes +- Parallel test execution +- Detailed test reports +- Easy-to-maintain test structure + +## Prerequisites + +Before you begin, ensure you have the following installed: + +- [Node.js](https://nodejs.org/) (v16 or later) +- [npm](https://www.npmjs.com/) or [yarn](https://yarnpkg.com/) + +## Installation + +1. Clone the repository: + + ```bash + git clone https://github.com/your-username/streamlyne-fundfit-test.git + cd streamlyne-fundfit-test + ``` + +2. Install dependencies: + `bash + npm install + ` + +## Usage + +### Running Tests + +To execute all tests in headless mode: + +```bash +npx playwright test +``` + +To run tests in a specific browser: + +```bash +npx playwright test --project=chromium +npx playwright test --project=firefox +npx playwright test --project=webkit +``` + +To run tests in headed mode: + +```bash +npx playwright test --headed +``` + +### Generating Reports + +After running tests, generate an HTML report: + +```bash +npm run allure:serve +``` + +### Debugging Tests + +Use the Playwright Inspector for debugging: + +```bash +npx playwright test --debug +``` + +## Project Structure + +``` +streamlyne-fundfit-test/ +├── pageObjectModel/ # Page Resources with Page Selectors +├── tests/ # Test files +├── testData/ # Storing test data on page resource basis +├── utilities/ # Resource needed for test management with custom action class +├── reports/ # Storing log and reporting resources +├── playwright.config.ts # Playwright configuration +├── .env # Storing Credentials and Sensitive information +├── package.json # Project metadata and dependencies +└── README.md # Project documentation +``` + +## Tools and Technologies + +- **Playwright**: End-to-end testing framework +- **TypeScript**: Strongly typed programming language +- **Jest**: Test runner (integrated with Playwright) +- **ESLint**: Linting tool for code quality +- **Prettier**: Code formatter +- **Allure Reporting**: Test Reporting + +## Contributing + +1. Fork the repository. +2. Create a new branch (`git checkout -b feature/your-feature`). +3. Commit your changes (`git commit -m 'Add your feature'`). + For questions or support, please contact [jvalle@streamlyne.com](mailto:jvalle@streamlyne.com). diff --git a/package-lock.json b/package-lock.json index 9dac95e..4e4fdbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,24 @@ { - "name": "playwright_ts_project", + "name": "playwright-hybrid-framework-stramlyne", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "playwright_ts_project", + "name": "playwright-hybrid-framework-stramlyne", "version": "1.0.0", "license": "ISC", "dependencies": { + "@faker-js/faker": "^9.7.0", "allure-js-commons": "^3.2.0", - "winston": "^3.17.0" + "dotenv": "^16.5.0", + "winston": "^3.17.0", + "zod": "^3.24.3" }, "devDependencies": { "@playwright/test": "^1.50.1", "@types/node": "^22.13.9", + "allure-commandline": "^2.34.0", "allure-playwright": "^3.0.9", "rimraf": "^6.0.1" } @@ -39,6 +43,22 @@ "kuler": "^2.0.0" } }, + "node_modules/@faker-js/faker": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.7.0.tgz", + "integrity": "sha512-aozo5vqjCmDoXLNUJarFZx2IN/GgGaogY4TMJ6so/WLZOWpSV7fvj2dmrV6sEAnUm1O7aCrhTibjpzeDFgNqbg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -86,6 +106,16 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/allure-commandline": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/allure-commandline/-/allure-commandline-2.34.0.tgz", + "integrity": "sha512-+SgVJ9+o7OH43KVCln0KT7VIsXCfMVEvFuYArIFNtS0fWu61UFdeiCYvrlQPC9trEoFhOe2e5i8Xhghwo46iRQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "allure": "bin/allure" + } + }, "node_modules/allure-js-commons": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.2.0.tgz", @@ -255,6 +285,18 @@ "node": "*" } }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -926,6 +968,15 @@ "engines": { "node": ">=8" } + }, + "node_modules/zod": { + "version": "3.24.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", + "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index ab8f84b..0a2c1fe 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,37 @@ { - "name": "playwright_ts_project", + "name": "playwright-hybrid-framework-stramlyne", "version": "1.0.0", "description": "creating playwright hybrid framework using Typescript.", "main": "index.js", "scripts": { "test:codegen": "npx playwright codegen", - "pretest": "npx playwright install && npx rimraf playwright-report test-results", - "test": "npx playwright test || npm run posttest", + "pretest": "npx playwright install && npx rimraf playwright-report test-results reports", + "test": "npx playwright test; npm run allure:serve", "testpw": "npx playwright test", "test:ui": "npx playwright test --ui", "test:chrome": "npx playwright test --project=chromium", "test:debug": "npx playwright test --debug", "test:headed": "npx playwright test --headed", - "posttest": "npx playwright show-report", - "dot:report": "npx playwright test --reporter=dot", - "line:report": "npx playwright test --reporter=line" + "posttest": "npx playwright show-report reports/html", + "allure:serve": "allure serve ./reports/allure-results", + "mcp": "npx @playwright/mcp@latest", + "playwright:install": "npx playwright install --with-deps" }, "keywords": [], - "author": "", + "author": "StreamLyne", "license": "ISC", "devDependencies": { "@playwright/test": "^1.50.1", "@types/node": "^22.13.9", + "allure-commandline": "^2.34.0", "allure-playwright": "^3.0.9", "rimraf": "^6.0.1" }, "dependencies": { + "@faker-js/faker": "^9.7.0", "allure-js-commons": "^3.2.0", - "winston": "^3.17.0" + "dotenv": "^16.5.0", + "winston": "^3.17.0", + "zod": "^3.24.3" } } diff --git a/pageObjectModel/dashboardPage.ts b/pageObjectModel/dashboardPage.ts new file mode 100644 index 0000000..106e96d --- /dev/null +++ b/pageObjectModel/dashboardPage.ts @@ -0,0 +1,162 @@ +import { Page } from "@playwright/test"; + +export class dashboardPage { + readonly dashboardButton: string; + readonly dashboardNavbarText: string; + readonly dashboardNavbarStartMatchingButton: string; + readonly dashboardNavbarMatchesButton: string; + readonly dashboardNavbarNotificationsButton: string; + readonly dashboardNavbarAccountButton: string; + readonly dashboardNavbarAccountTexts: string; + readonly dashboardNavbarAccountProfileText: string; + readonly dashboardNavbarAccountAdminText: string; + readonly dashboardNavbarAccountLogoutText: string; + readonly dashboardNavbarAccountSubmitFeedbackText: string; + + readonly dashboardAllOpportunitiesCard: string; + + readonly dashboardAllCardHeaderText: string; + + readonly dashboardAllOpportunitiesCardText: string; + readonly dashboardNewResearchersOpportunitiesCardText: string; + readonly dashboardExpireResearchersOpportunitiesCardText: string; + readonly dashboardFoundingAvailableCardText: string; + readonly dashboardAllResearchersCardText: string; + readonly dashboardFoundingScenarioText: string; + readonly dashboardRecentMatchNotificationsText: string; + + readonly dashboardFoundingScenarioButton: string; + readonly dashboardRecentMatchNotificationsButton: string; + + readonly dashboardAllOpportunitiesIcon: string; + readonly dashboardResearchersOpportunitiesIcon: string; + readonly dashboardFoundingAvailableIcon: string; + readonly dashboardAllResearchersIcon: string; + readonly dashboardFoundingScenarioIcon: string; + readonly dashboardRecentMatchNotificationsIcon: string; + + readonly dashboardAllOpportunitiesValue: string; + readonly dashboardNewResearchersOpportunitiesValue: string; + readonly dashboardExpireResearchersOpportunitiesValue: string; + readonly dashboardFoundingAvailablePotentialValue: string; + readonly dashboardFoundingAvailableTotalValue: string; + readonly dashboardAllResearchersValue: string; + + readonly dashboardFoundingScenarioRechart: string; + + readonly dashboardNotificationTitle: string; + readonly dashboardNotificationText: string; + readonly dashboardNotificationViewButton: string; + readonly dashboardNotificationButtonIcon: string; + + readonly dashboardNotificationLists: string; + readonly dashboardNoNotificationText: string; + + // Dashboard NotificationModal + readonly notificationModalHeader: string; + readonly notificationModalCloseIcon: string; + readonly notificationModalNameLabel: string; + readonly notificationModalName: string; + readonly notificationModalNotificationTypeSelectLabel: string; + readonly notificationModalNotificationType: string; + readonly notificationModalUsersEmailSelectLabel: string; + readonly notificationModalUsersEmail: string; + readonly notificationModalEmailCheckboxLabel: string; + readonly notificationModalEmailCheckboxText: string; + readonly notificationModalEmailCheckbox: string; + readonly notificationModalMatchTypeSelectLabel: string; + readonly notificationModalMatchTypeSelect: string; + readonly notificationModalOpportunitiesSelectLabel: string; + readonly notificationModalOpportunitiesSelect: string; + readonly notificationModalSelectCollaborationLabel: string; + readonly notificationModalSelectCollaborationIcon: string; + readonly notificationModalSelectCollaborationText: string; + readonly notificationModalFindCollaborationHeaderIcon: string; + readonly notificationModalFindCollaborationHeader: string; + readonly notificationModalFindCollaborationLabel: string; + readonly notificationModalFindCollaborationSelect: string; + readonly notificationModalFindCollaborationText: string; + readonly notificationModalCloseButton: string; + + readonly dashboardCardLoader: string; + + constructor(page: Page) { + this.dashboardButton = `css=a[data-intro='navbar-dashboard']`; + this.dashboardNavbarText = `css=header[class='sticky border-b-[1px] bg-white shadow-sm top-0 z-40 w-full bg-blue-50'] a[data-intro]`; + this.dashboardNavbarStartMatchingButton = `css=div[class='hidden md:flex items-center gap-4'] a:nth-child(1)`; + this.dashboardNavbarMatchesButton = `css=a[data-intro='navbar-matchlist']`; + this.dashboardNavbarNotificationsButton = `css=a[data-intro='navbar-notification']`; + this.dashboardNavbarAccountButton = `css=nav[class='hidden md:flex justify-end items-center gap-4'] div[type='button']`; + this.dashboardNavbarAccountTexts = `css=a[role="menuitem"]`; + this.dashboardNavbarAccountProfileText = `css=a[href='/profile']`; + this.dashboardNavbarAccountAdminText = `css=a[href='/organization-admin']`; + this.dashboardNavbarAccountLogoutText = `css=a[href='/logout']`; + this.dashboardNavbarAccountSubmitFeedbackText = `css=div[class='relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 w-full flex items-center gap-2 cursor-pointer']`; + + this.dashboardAllOpportunitiesCard = `css=div[data-intro='opportunities-count'] div[class='tracking-tight text-sm font-medium']`; + + this.dashboardAllCardHeaderText = `css=div[class='grid gap-4 md:grid-cols-2 lg:grid-cols-4 w-full'] div div[class='p-6 flex flex-row items-center justify-between space-y-0 pb-2']`; + + this.dashboardAllOpportunitiesCardText = `css=div[data-intro='opportunities-count'] p[class='text-xs text-muted-foreground']`; + this.dashboardNewResearchersOpportunitiesCardText = `css=body > div:nth-child(1) > div:nth-child(1) > main:nth-child(2) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > p:nth-child(2)`; + this.dashboardExpireResearchersOpportunitiesCardText = `css=body > div:nth-child(1) > div:nth-child(1) > main:nth-child(2) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > div:nth-child(2) > div:nth-child(1) > div:nth-child(2) > p:nth-child(2)`; + this.dashboardFoundingAvailableCardText = `css=div[data-intro='funding-available'] p[class='text-xs text-muted-foreground']`; + this.dashboardAllResearchersCardText = `css=div[data-intro='researchers-count'] p[class='text-xs text-muted-foreground']`; + this.dashboardFoundingScenarioText = `css=div[data-intro='scenario-status'] p[class='text-xs text-muted-foreground']`; + this.dashboardRecentMatchNotificationsText = `css=div[data-intro='recent-notifications'] div[class='flex items-center'] p[class='text-xs text-muted-foreground']`; + + this.dashboardFoundingScenarioButton = `css=div[data-intro='scenario-status'] span[class='text-sm font-bold text-blue-600 cursor-pointer hover:underline']`; + this.dashboardRecentMatchNotificationsButton = `css=div[data-intro='recent-notifications'] span[class='text-sm font-bold text-blue-600 cursor-pointer hover:underline']`; + + this.dashboardAllOpportunitiesIcon = `css=svg[class='lucide lucide-dollar-sign']`; + this.dashboardResearchersOpportunitiesIcon = `css=svg[class='lucide lucide-hand-coins']`; + this.dashboardFoundingAvailableIcon = `css=svg[class='lucide lucide-circle-dollar-sign']`; + this.dashboardAllResearchersIcon = `css=svg[class='lucide lucide-circle-help h-4 w-4 text-blue-600']`; + this.dashboardFoundingScenarioIcon = `css=svg[class='lucide lucide-folder-kanban']`; + this.dashboardRecentMatchNotificationsIcon = `css=svg[class='lucide lucide-bell-ring']`; + + this.dashboardAllOpportunitiesValue = `css=div[data-intro='opportunities-count'] div[class='text-2xl font-bold h-[1.75rem] flex items-center']`; + this.dashboardNewResearchersOpportunitiesValue = `css=div[class='text-2xl font-bold text-blue-600 h-[1.75rem] flex items-center'] span`; + this.dashboardExpireResearchersOpportunitiesValue = `css=div[class='text-2xl font-bold text-amber-500 h-[1.75rem] flex items-center'] span`; + this.dashboardFoundingAvailablePotentialValue = `css=span[class='text-2xl font-bold text-blue-600']`; + this.dashboardFoundingAvailableTotalValue = `css=span[class='ml-1 text-muted-foreground']`; + this.dashboardAllResearchersValue = `css=div[data-intro="dashboard-welcome"] div[data-intro="researchers-count"] div[class="p-6 pt-0"] div span`; + + this.dashboardFoundingScenarioRechart = `css=div[class='recharts-wrapper']`; + + this.dashboardNotificationTitle = `css=div[data-intro='recent-notifications'] div:nth-child(2) div:nth-child(2) div p:nth-child(1)`; + this.dashboardNotificationText = `css=div[data-intro='recent-notifications'] div:nth-child(2) div:nth-child(2) div p:nth-child(2)`; + this.dashboardNotificationViewButton = `css=body > div:nth-child(1) > div:nth-child(1) > main:nth-child(2) > div:nth-child(1) > div:nth-child(1) > div:nth-child(6) > div:nth-child(2) > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > button:nth-child(1)`; + this.dashboardNotificationButtonIcon = `css=button[class='justify-center gap-2 whitespace-nowrap ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-blue-600 text-white px-3 py-1 rounded-full hover:bg-blue-700 text-xs font-medium flex items-center h-auto'] svg[class='lucide lucide-eye ml-1']`; + + this.dashboardNotificationLists = `css=div[data-intro='recent-notifications'] div[class='pt-0 mt-5'] div[class='space-y-4'] div div p:first-child`; + this.dashboardNoNotificationText = `css=div[data-intro='recent-notifications'] div[class='flex flex-col items-center justify-center py-8 text-center']`; + + this.notificationModalHeader = `css=div[class='flex justify-between items-center'] h2[class='text-2xl font-bold']`; + this.notificationModalCloseIcon = `css=button[class='absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground']`; + this.notificationModalNameLabel = `xpath=//label[normalize-space()='Name']`; + this.notificationModalName = `css=input[name='name']`; + this.notificationModalNotificationTypeSelectLabel = `xpath=//label[normalize-space()='Notification Type']`; + this.notificationModalNotificationType = `css=button[id=':rmg:-form-item']`; + this.notificationModalUsersEmailSelectLabel = `xpath=//label[normalize-space()='Select Email Users']`; + this.notificationModalUsersEmail = `css=button[id=':rbt:-form-item']`; + this.notificationModalEmailCheckboxLabel = `xpath=//label[normalize-space()='Confirm receiving emails?']`; + this.notificationModalEmailCheckboxText = `css=div[class='space-y-1 leading-none'] p`; + this.notificationModalEmailCheckbox = `css=button[id=':rbv:-form-item']`; + this.notificationModalMatchTypeSelectLabel = `xpath=//label[normalize-space()='Match Type']`; + this.notificationModalMatchTypeSelect = `css=button[id=':rc0:-form-item']`; + this.notificationModalOpportunitiesSelectLabel = `xpath=//label[normalize-space()='Opportunity Search']`; + this.notificationModalOpportunitiesSelect = `css=button[id=':rc2:-form-item']`; + this.notificationModalSelectCollaborationLabel = `xpath=//label[normalize-space()='Select Collaboration Institutions']`; + this.notificationModalSelectCollaborationIcon = `css=svg[class='lucide lucide-circle-alert text-destructive mr-1']`; + this.notificationModalSelectCollaborationText = `css=p[id=':rfc:-form-item-description']`; + this.notificationModalFindCollaborationHeaderIcon = `css=svg[class='lucide lucide-building2 h-5 w-5 text-blue-600']`; + this.notificationModalFindCollaborationHeader = `css=h2[class='text-2xl font-semibold text-blue-600']`; + this.notificationModalFindCollaborationLabel = `css=label[class='text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 mb-2 block']`; + this.notificationModalFindCollaborationSelect = `css=.y-between.group.flex.w-full.rounded-md.border.border-input.bg-transparent.px-3.py-1.text-sm.shadow-sm.transition-colors.justify-between.min-h-10.h-auto.cursor-not-allowed.opacity-50.mb-2`; + this.notificationModalFindCollaborationText = `css=div[class='text-sm text-gray-500'] span`; + this.notificationModalCloseButton = `css=button[class='inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2']`; + + this.dashboardCardLoader = `css=div span[class='flex-col items-center justify-center flex']`; + } +} diff --git a/pageObjectModel/homePage.ts b/pageObjectModel/homePage.ts new file mode 100644 index 0000000..7e63437 --- /dev/null +++ b/pageObjectModel/homePage.ts @@ -0,0 +1,41 @@ +import { Page } from "playwright"; + +export class homePage { + readonly homePageCanvas: string; + readonly loginButton: string; + readonly registerButton: string; + readonly headerImage: string; + readonly homePageHeroText: string; + readonly homePageSubHeroText: string; + readonly startTrialButton: string; + readonly viewPricingButton: string; + readonly pricingSectionHeroText: string; + readonly pricingSectionSubHeroText: string; + readonly researcherBlockHeader: string; + readonly administratorBlockHeader: string; + readonly researcherPriceText: string; + readonly pricingSectionLinkTexts: string; + readonly getStartedButton: string; + readonly contactSalesButton: string; + readonly footerBlock: string; + + constructor(page: Page) { + this.homePageCanvas = `css=canvas[width='1472']`; + this.loginButton = `css=a[data-intro="navbar-notification"][data-discover="true"][href="/login"]`; + this.registerButton = `css=a[data-intro="navbar-notification"][href="/register"][data-discover="true"]`; + this.headerImage = `css=div[class='flex items-center gap-4'] img[alt='FundFit Logo']`; + this.homePageHeroText = `css=h1[class='text-3xl md:text-4xl lg:text-5xl font-bold tracking-tight text-gray-900 break-normal']`; + this.homePageSubHeroText = `css=p[class='text-lg md:text-xl text-gray-600 leading-relaxed break-normal']`; + this.startTrialButton = `css=div[class='flex flex-col sm:flex-row gap-4']> a:nth-child(1)`; + this.viewPricingButton = `css=div[class='flex flex-col sm:flex-row gap-4']> button`; + this.researcherPriceText = `css=span[class='text-3xl font-bold text-orange-600']`; + this.pricingSectionHeroText = `css=h2[class='text-3xl md:text-4xl font-bold text-gray-900 mb-4']`; + this.pricingSectionSubHeroText = `css=.text-xl.text-gray-600`; + this.footerBlock = `css=footer[class='text-center py-4'] span:nth-child(1)`; + this.researcherBlockHeader = `css=div[class='grid grid-cols-1 md:grid-cols-2 gap-6 max-w-5xl mx-auto'] div:nth-child(1) div:nth-child(1) h4:nth-child(1)`; + this.administratorBlockHeader = `css=div[class='grid grid-cols-1 md:grid-cols-2 gap-6 max-w-5xl mx-auto'] div:nth-child(2) div:nth-child(1) h4:nth-child(1)`; + this.pricingSectionLinkTexts = `css=ul[class='space-y-3 mb-8'] li[class='flex items-start text-sm text-gray-600']`; + this.getStartedButton = `css=a[class='block'] button[class='inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 h-11 rounded-md px-8 w-full bg-primary text-white font-bold hover:bg-orange-600']`; + this.contactSalesButton = `css=a[href='https://streamlyne.com/fundfit/']`; + } +} diff --git a/pageObjectModel/loginPage.ts b/pageObjectModel/loginPage.ts new file mode 100644 index 0000000..174f082 --- /dev/null +++ b/pageObjectModel/loginPage.ts @@ -0,0 +1,41 @@ +import { Page } from "playwright"; + +export class loginPage { + readonly email: string; + readonly password: string; + readonly eyeIcon: string; + readonly loginButton: string; + readonly toastMessage: string; + readonly successfulLoginMessage: string; + readonly emailInputBoxHeader: string; + readonly passwordInputBoxHeader: string; + readonly emailInputBoxErrorMessage: string; + readonly passwordInputBoxErrorMessage: string; + readonly loginPageRegisterButton: string; + readonly loginPageHeaderText: string; + readonly forgetPasswordButton: string; + readonly forgetPasswordFormHeaderText: string; + readonly ForgetPasswordEmailHeader: string; + readonly ForgetPasswordEmailInputBox: string; + readonly forgetPasswordFormSubHeaderText: string; + + constructor(page: Page) { + this.loginPageHeaderText = + "css=div[class='flex flex-col space-y-1.5 p-6'] div:nth-child(2)"; + this.email = `css=input[type='email']`; + this.password = `css=input[name='password']`; + this.eyeIcon = `css=button[class='inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:text-accent-foreground h-9 rounded-md px-3 ml-auto hover:bg-transparent']`; + this.loginButton = `css=button[type='submit']`; + this.toastMessage = `css=div[class='grid gap-1']`; + this.emailInputBoxHeader = `xpath=//label[normalize-space()='Email Address']`; + this.passwordInputBoxHeader = `xpath=//label[normalize-space()='Password']`; + this.emailInputBoxErrorMessage = `xpath=(//p[@id=':re:-form-item-message'])[1]`; + this.passwordInputBoxErrorMessage = `xpath= //p[@id=':rf:-form-item-message']`; + this.loginPageRegisterButton = `css=div[class='text-sm text-muted-foreground m-auto'] a[href='/register']`; + this.forgetPasswordButton = `css=a[href='/forgot-password']`; + this.forgetPasswordFormHeaderText = `css=div[class='flex flex-col space-y-1.5 p-6'] div:nth-child(2)`; + this.forgetPasswordFormSubHeaderText = `css=div[class='flex flex-col space-y-1.5 p-6'] div:nth-child(3)`; + this.ForgetPasswordEmailHeader = `css=div[class="space-y-2"] label`; + this.ForgetPasswordEmailInputBox = `css=input[id=':ru:-form-item']`; + } +} diff --git a/pageObjectModel/matchesPage.ts b/pageObjectModel/matchesPage.ts new file mode 100644 index 0000000..4b12449 --- /dev/null +++ b/pageObjectModel/matchesPage.ts @@ -0,0 +1,9 @@ +import { Page } from "@playwright/test"; + +export class matchesPage { + readonly matchesHeroText: string; + + constructor(page: Page) { + this.matchesHeroText = `css=h1[class='text-2xl font-semibold']`; + } +} diff --git a/pageObjectModel/notificationPage.ts b/pageObjectModel/notificationPage.ts new file mode 100644 index 0000000..1dfd4ae --- /dev/null +++ b/pageObjectModel/notificationPage.ts @@ -0,0 +1,9 @@ +import { Page } from "@playwright/test"; + +export class notificationPage { + readonly notificationHeroText: string; + + constructor(page: Page) { + this.notificationHeroText = `css=h1[class='text-2xl font-semibold']`; + } +} diff --git a/pageObjectModel/profilePage.ts b/pageObjectModel/profilePage.ts new file mode 100644 index 0000000..83f2c15 --- /dev/null +++ b/pageObjectModel/profilePage.ts @@ -0,0 +1,389 @@ +import { Page } from "playwright"; + +export class profilePage { + readonly sidebarPhoto: string; + readonly sidebarFullNameText: string; + readonly sidebarTitleText: string; + + readonly sidebarGeneralInfoButton: string; + readonly sidebarBackgroundButton: string; + readonly sidebarWorksButton: string; + readonly sidebarProjectsButton: string; + readonly sidebarAttachmentsButton: string; + readonly sidebarORCIDButton: string; + + readonly sidebarGeneralInfoIcon: string; + readonly sidebarBackgroundIcon: string; + readonly sidebarWorksIcon: string; + readonly sidebarProjectsIcon: string; + readonly sidebarAttachmentsIcon: string; + readonly sidebarORCIDIcon: string; + + readonly sidebarGeneralInfoText: string; + readonly sidebarBackgroundText: string; + readonly sidebarWorksText: string; + readonly sidebarProjectsText: string; + readonly sidebarAttachmentsText: string; + readonly sidebarORCIDText: string; + + readonly sidebarBackgroundValue: string; + readonly sidebarWorksValue: string; + readonly sidebarProjectsValue: string; + readonly sidebarAttachmentsValue: string; + + readonly topBarGeneralButton: string; + readonly topBarBackgroundButton: string; + readonly topBarWorksButton: string; + readonly topBarProjectsButton: string; + readonly topBarAttachmentsButton: string; + + readonly profileHeaderText: string; + readonly editProfileIcon: string; + readonly editProfile: string; + readonly passwordSettingsIcon: string; + readonly passwordSettings: string; + readonly emailSettingsIcon: string; + readonly emailSettings: string; + + readonly firstName: string; + readonly lastName: string; + readonly email: string; + readonly institution: string; + readonly phoneNumber: string; + readonly biography: string; + + readonly firstNameHeader: string; + readonly lastNameHeader: string; + readonly emailHeader: string; + readonly institutionHeader: string; + readonly phoneNumberHeader: string; + readonly biographyHeader: string; + + readonly editProfileHeaderText: string; + readonly editProfileFirstName: string; + readonly editProfileLastName: string; + readonly editProfilePhoneNumber: string; + readonly editProfileBiography: string; + readonly editProfileFirstNameHeaderText: string; + readonly editProfileFirstNamePlaceHolderText: string; + readonly editProfileFirstNameErrorText: string; + readonly editProfileLastNameHeaderText: string; + readonly editProfileLastNameErrorText: string; + readonly editProfilePhoneNumberHeaderText: string; + readonly editProfilePhoneNumberErrorText: string; + readonly editProfilePhoneNumberErrorText2: string; + readonly editProfileBiographyHeaderText: string; + + readonly passwordSettingsHeaderText: string; + readonly passwordSettingsNewPasswordHeaderText: string; + readonly passwordSettingsNewPasswordInput: string; + readonly passwordSettingsNewPasswordEyeIconButton: string; + readonly passwordSettingsConfirmNewPasswordHeaderText: string; + readonly passwordSettingsConfirmNewPasswordInput: string; + readonly passwordSettingsConfirmNewPasswordErrorText: string; + readonly passwordSettingsConfirmNewPasswordErrorText2: string; + + readonly emailSettingsHeaderText: string; + readonly emailSettingsCurrentEmailHeaderText: string; + readonly emailSettingsCurrentEmailText: string; + readonly emailSettingsEditEmailButton: string; + readonly emailSettingsEditEmailHeaderText: string; + readonly emailSettingsNewEmailFrom: string; + readonly emailSettingsAlreadyVerifiedErrorText: string; + readonly emailSettingsInvalidErrorText: string; + + readonly backgroundHeaderText: string; + readonly backgroundPlusIconButton: string; + readonly noBackgroundIcon: string; + readonly noBackgroundHeaderText: string; + readonly noBackgroundText: string; + readonly noBackgroundAddButton: string; + + readonly createBackgroundHeaderText: string; + readonly createBackgroundTypeHeaderText: string; + readonly createBackgroundTypeButtonText: string; + readonly createBackgroundTypeErrorText: string; + readonly createBackgroundTypeTexts: string; + readonly createBackgroundRoleInput: string; + readonly createBackgroundDepartmentInput: string; + readonly createBackgroundOrganizationInput: string; + readonly createBackgroundOrganizationAddressInput: string; + readonly createBackgroundStartYearInput: string; + readonly createBackgroundStartMonthInput: string; + readonly createBackgroundStartDayInput: string; + readonly createBackgroundEndYearInput: string; + readonly createBackgroundEndMonthInput: string; + readonly createBackgroundEndDayInput: string; + + readonly worksHeaderText: string; + readonly worksPlusIconButton: string; + readonly noWorksIcon: string; + readonly noWorksHeaderText: string; + readonly noWorksText: string; + readonly noWorksAddButton: string; + + readonly createWorkHeaderText: string; + readonly createWorkTypeHeaderText: string; + readonly createWorkTypeButtonText: string; + readonly createWorkTypeErrorText: string; + readonly createWorkJournalTitleHeaderText: string; + readonly createWorkJournalTitleInput: string; + readonly createWorkJournalTitleErrorText: string; + readonly createWorkTitleHeaderText: string; + readonly createWorkTitleInput: string; + readonly createWorkTitleErrorText: string; + readonly createWorkPublicationYearHeaderText: string; + readonly createWorkPublicationYearInput: string; + readonly createWorkPublicationMonthHeaderText: string; + readonly createWorkPublicationMonthInput: string; + readonly createWorkPublicationDayHeaderText: string; + readonly createWorkPublicationDayInput: string; + + readonly projectsHeaderText: string; + readonly projectsPlusIconButton: string; + readonly noProjectsIcon: string; + readonly noProjectsHeaderText: string; + readonly noProjectsText: string; + readonly noProjectsAddButton: string; + + readonly createProjectsHeaderTextSection: string; + readonly createProjectsTypeHeaderText: string; + readonly createProjectsTypeButtonText: string; + readonly createProjectsTitleHeaderText: string; + readonly createProjectsTitleInput: string; + readonly createProjectsOrgHeaderText: string; + readonly createProjectsOrgInput: string; + readonly createProjectsOrgTypeHeaderText: string; + readonly createProjectsOrgTypeInput: string; + readonly createProjectsOrgLocationHeaderText: string; + readonly createProjectsOrgLocationInput: string; + readonly createProjectsOrgAmountHeaderText: string; + readonly createProjectsOrgAmountInput: string; + readonly createProjectsStartYearHeaderText: string; + readonly createProjectsStartYearInput: string; + readonly createProjectsStartMonthHeaderText: string; + readonly createProjectsStartMonthInput: string; + readonly createProjectsStartDayHeaderText: string; + readonly createProjectsStartDayInput: string; + readonly createProjectsEndYearHeaderText: string; + readonly createProjectsEndYearInput: string; + readonly createProjectsEndMonthHeaderText: string; + readonly createProjectsEndMonthInput: string; + readonly createProjectsEndDayHeaderText: string; + readonly createProjectsEndDayInput: string; + readonly selectProjectTypeComboBoxInput: string; + readonly selectProjectTypeComboBoxTextValues: string; + + readonly noDataFoundTexts: string; + readonly createTypeSearchInput: string; // Added for combobox search + readonly noResultsFoundMessage: string; // Added for "No results found" message + + readonly loadingRecordsText: string; + readonly table: string; + readonly tableUpdateButton: string; + readonly tableDeleteButton: string; + readonly deleteModalHeaderText: string; + readonly deleteModalButton: string; + + readonly toastMessageHeaderText: string; + readonly toastMessageText: string; + readonly toastCloseButton: string; + readonly backAndCancelButton: string; + readonly submitButton: string; + + constructor(page: Page) { + this.sidebarPhoto = `css=span[class='flex h-full w-full items-center justify-center rounded-full bg-primary text-primary-foreground text-xl font-semibold']`; + this.sidebarFullNameText = `css=h2[class='text-lg font-semibold mb-1']`; + this.sidebarTitleText = `css=p[class='text-sm text-muted-foreground mb-6']`; + + this.sidebarGeneralInfoButton = `css=nav[class='w-full space-y-1'] > button:nth-child(1)`; + this.sidebarBackgroundButton = `css=nav[class='w-full space-y-1'] > button:nth-child(3)`; + this.sidebarWorksButton = `css=nav[class='w-full space-y-1'] > button:nth-child(5)`; + this.sidebarProjectsButton = `css=nav[class='w-full space-y-1'] > button:nth-child(7)`; + this.sidebarAttachmentsButton = `css=nav[class='w-full space-y-1'] > button:nth-child(9)`; + this.sidebarORCIDButton = `css=div[class='w-full lg:w-72 shrink-0'] div[class='mt-auto pt-6 w-full'] button`; + + this.sidebarGeneralInfoText = `css=nav[class='w-full space-y-1'] > button:nth-child(1) > span:nth-child(2)`; + this.sidebarBackgroundText = `css=nav[class='w-full space-y-1'] > button:nth-child(3) > span:nth-child(2)`; + this.sidebarWorksText = `css=nav[class='w-full space-y-1'] > button:nth-child(5) > span:nth-child(2)`; + this.sidebarProjectsText = `css=nav[class='w-full space-y-1'] > button:nth-child(7) > span:nth-child(2)`; + this.sidebarAttachmentsText = `css=nav[class='w-full space-y-1'] > button:nth-child(9) > span:nth-child(2)`; + this.sidebarORCIDText = `css=div[class='w-full lg:w-72 shrink-0'] div[class='mt-auto pt-6 w-full'] button`; + + this.sidebarGeneralInfoIcon = `css=nav[class='w-full space-y-1'] > button:nth-child(1) > span:nth-child(1)`; + this.sidebarBackgroundIcon = `css=nav[class='w-full space-y-1'] > button:nth-child(3) > span:nth-child(1)`; + this.sidebarWorksIcon = `css=nav[class='w-full space-y-1'] > button:nth-child(5) > span:nth-child(1)`; + this.sidebarProjectsIcon = `css=nav[class='w-full space-y-1'] > button:nth-child(7) > span:nth-child(1)`; + this.sidebarAttachmentsIcon = `css=nav[class='w-full space-y-1'] > button:nth-child(9) > span:nth-child(1)`; + this.sidebarORCIDIcon = `css=div[class='w-full lg:w-72 shrink-0'] div[class='mt-auto pt-6 w-full'] button`; + + this.sidebarBackgroundValue = `css=nav[class='w-full space-y-1'] > button:nth-child(3) > span:nth-child(3)`; + this.sidebarWorksValue = `css=nav[class='w-full space-y-1'] > button:nth-child(5) > span:nth-child(3)`; + this.sidebarProjectsValue = `css=nav[class='w-full space-y-1'] > button:nth-child(7) > span:nth-child(3)`; + this.sidebarAttachmentsValue = `css=nav[class='w-full space-y-1'] > button:nth-child(9) > span:nth-child(3)`; + + this.topBarGeneralButton = `css=div[class='inline-flex items-center p-1 text-muted-foreground h-14 w-full justify-start rounded-none bg-transparent border-b px-6'] button:nth-child(1)`; + this.topBarBackgroundButton = `css=div[class='inline-flex items-center p-1 text-muted-foreground h-14 w-full justify-start rounded-none bg-transparent border-b px-6'] button:nth-child(2)`; + this.topBarWorksButton = `css=div[class='inline-flex items-center p-1 text-muted-foreground h-14 w-full justify-start rounded-none bg-transparent border-b px-6'] button:nth-child(3)`; + this.topBarProjectsButton = `css=div[class='inline-flex items-center p-1 text-muted-foreground h-14 w-full justify-start rounded-none bg-transparent border-b px-6'] button:nth-child(4)`; + this.topBarAttachmentsButton = `css=div[class='inline-flex items-center p-1 text-muted-foreground h-14 w-full justify-start rounded-none bg-transparent border-b px-6'] button:nth-child(5)`; + + this.profileHeaderText = `css=div[class='w-full max-w-3xl'] div h1`; + this.editProfileIcon = `css=div[class='flex gap-3'] svg[class='lucide lucide-square-pen']`; + this.editProfile = `css=div[class='flex gap-3'] a[href='/profile/settings']`; + this.passwordSettings = `css=div[class='flex gap-3'] svg[class='lucide lucide-lock']`; + this.passwordSettings = `css=div[class='flex gap-3'] a[href='/profile/settings/password']`; + this.emailSettings = `css=div[class='flex gap-3'] svg[class='lucide lucide-mail']`; + this.emailSettings = `css=div[class='flex gap-3'] a[href='/profile/settings/email']`; + + this.firstName = `css=div[class='space-y-6'] div div:nth-child(1) p`; + this.lastName = `css=div[class='space-y-6'] div div:nth-child(2) p`; + this.email = `css=div[class='space-y-6'] > div:nth-child(2) p`; + this.institution = `css=div[class='space-y-6'] > div:nth-child(3) p`; + this.phoneNumber = `css=div[class='space-y-6'] > div:nth-child(4) p`; + this.biography = `css=div[class='space-y-6'] > div:nth-child(5) p`; + + this.firstNameHeader = `css=div[class='space-y-6'] div div:nth-child(1) h3`; + this.lastNameHeader = `css=div[class='space-y-6'] div div:nth-child(2) h3`; + this.emailHeader = `css=div[class='space-y-6'] > div:nth-child(2) h3`; + this.institutionHeader = `css=div[class='space-y-6'] > div:nth-child(3) h3`; + this.phoneNumberHeader = `css=div[class='space-y-6'] > div:nth-child(4) h3`; + this.biographyHeader = `css=div[class='space-y-6'] > div:nth-child(5) h3`; + + this.editProfileHeaderText = `xpath=//h1[normalize-space()='Update Profile']`; + this.editProfileFirstName = `css=input[name='first_name']`; + this.editProfileLastName = `css=input[name='last_name']`; + this.editProfilePhoneNumber = `css=input[name='phone_number']`; + this.editProfileBiography = `css=textarea[name='biography']`; + this.editProfileFirstNameHeaderText = `xpath=//label[normalize-space()='First Name*']`; + this.editProfileFirstNamePlaceHolderText = `xpath=//p[normalize-space()='First name is required']`; + this.editProfileFirstNameErrorText = `css=div[class='flex space-x-4'] div[class='space-y-2 w-1/2'] p[class='text-sm font-medium text-destructive']`; + this.editProfileLastNameHeaderText = `xpath=//label[normalize-space()='Last Name*']`; + this.editProfileLastNameErrorText = `css=div[class='space-y-2 w-1/2'] p[class='text-sm font-medium text-destructive']`; + this.editProfilePhoneNumberHeaderText = `xpath=//label[normalize-space()='Phone Number']`; + this.editProfilePhoneNumberErrorText = `xpath=//p[normalize-space()='Phone number must contain only numbers']`; + this.editProfilePhoneNumberErrorText2 = `xpath=//p[normalize-space()='Ensure this field has no more than 15 characters.']`; + this.editProfileBiographyHeaderText = `xpath=//label[normalize-space()='Biography']`; + + this.passwordSettingsHeaderText = `xpath=//h1[normalize-space()='Change Password']`; + this.passwordSettingsNewPasswordHeaderText = `xpath=//label[normalize-space()='New Password*']`; + this.passwordSettingsNewPasswordInput = `css=input[name='new_password1']`; + this.passwordSettingsNewPasswordEyeIconButton = `"css=button[class='inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:text-accent-foreground h-9 rounded-md px-3 ml-2 hover:bg-transparent']"`; + this.passwordSettingsConfirmNewPasswordHeaderText = `xpath=//label[normalize-space()='Confirm New Password*']`; + this.passwordSettingsConfirmNewPasswordInput = `css=input[name='new_password2']`; + this.passwordSettingsConfirmNewPasswordErrorText = `css=div[class="space-y-2"] p[class="text-sm font-medium text-destructive"]`; + this.passwordSettingsConfirmNewPasswordErrorText2 = `css=div[class="text-sm text-red-500 font-medium"]`; + + this.emailSettingsHeaderText = `xpath=//h1[normalize-space()='Email Settings']`; + this.emailSettingsCurrentEmailHeaderText = `css=h2[class='text-sm font-medium']`; + this.emailSettingsCurrentEmailText = `css=span[class='text-sm']`; + this.emailSettingsEditEmailButton = `css=div[class='flex gap-3'] button`; + this.emailSettingsEditEmailHeaderText = `css=h2[class='text-lg font-semibold']`; + this.emailSettingsNewEmailFrom = `css=form[class='space-y-6'] div[class='space-y-2']`; + this.emailSettingsAlreadyVerifiedErrorText = `css=form[class='space-y-6'] div[class='text-sm text-red-500 font-medium']`; + this.emailSettingsInvalidErrorText = `css=form[class='space-y-6'] p[class='text-sm font-medium text-destructive']`; + + this.backgroundHeaderText = `xpath=//h1[normalize-space()='Background']`; + this.backgroundPlusIconButton = `css=div[class="flex justify-between items-center mb-6"] a[href="/profile/background/create"]`; + this.noBackgroundIcon = `css=div svg[class='lucide lucide-graduation-cap w-8 h-8']`; + this.noBackgroundHeaderText = `css=h2[class='text-xl font-semibold']`; + this.noBackgroundText = `css=p[class='text-gray-500 max-w-md break-normal']`; + this.noBackgroundAddButton = `css=div[class='bg-white rounded-lg p-8 border border-gray-200 flex flex-col items-center justify-center text-center space-y-4'] a[href="/profile/background/create"]`; + + this.createBackgroundHeaderText = `xpath=//h1[normalize-space()='Create Background']`; + this.createBackgroundTypeHeaderText = `css=div[class='flex space-x-4']:first-child div[class='space-y-2 w-1/3'] label`; + this.createBackgroundTypeButtonText = `css=button[role='combobox']`; + this.createBackgroundTypeErrorText = `css=div[class='space-y-2 w-1/3'] p`; + this.createBackgroundTypeTexts = `css=div[role='presentation'] div[role='group'] div`; + this.createBackgroundRoleInput = `css=input[name='role']`; + this.createBackgroundDepartmentInput = `css=input[name='department']`; + this.createBackgroundOrganizationInput = `css=input[name='organization']`; + this.createBackgroundOrganizationAddressInput = `css=input[name='organization_address']`; + this.createBackgroundStartYearInput = `css=input[name='start_year']`; + this.createBackgroundStartMonthInput = `css=input[name='start_month']`; + this.createBackgroundStartDayInput = `css=input[name='start_day']`; + this.createBackgroundEndYearInput = `css=input[name='end_year']`; + this.createBackgroundEndMonthInput = `css=input[name='end_month']`; + this.createBackgroundEndDayInput = `css=input[name='end_day']`; + + this.worksHeaderText = `xpath=//h1[normalize-space()='Research Works']`; + this.worksPlusIconButton = `css=div[class="flex justify-between items-center mb-6"] a[href="/profile/work/create"]`; + this.noWorksIcon = `css=div svg[class='lucide lucide-book-open w-8 h-8']`; + this.noWorksHeaderText = `css=h2[class='text-xl font-semibold']`; + this.noWorksText = `css=p[class='text-gray-500 max-w-md break-normal']`; + this.noWorksAddButton = `css=div[class='bg-white rounded-lg p-8 border border-gray-200 flex flex-col items-center justify-center text-center space-y-4'] a[href="/profile/work/create"]`; + + this.createWorkHeaderText = `xpath=//h1[normalize-space()='Create Research Work']`; + this.createWorkTypeHeaderText = `css=div[class='flex space-x-4']:nth-child(1) div[class='space-y-2 w-1/3'] label`; + this.createWorkTypeButtonText = `css=button[role='combobox']`; + this.createWorkTypeErrorText = `css=div[class='space-y-2 w-1/3'] p[class='text-sm font-medium text-destructive']`; + this.createWorkJournalTitleHeaderText = `css=div[class='flex space-x-4']:nth-child(1) div[class='space-y-2 w-2/3'] label`; + this.createWorkJournalTitleInput = `css=input[name='journal_title']`; + this.createWorkJournalTitleErrorText = `css=div[class='space-y-2 w-2/3'] p[class='text-sm font-medium text-destructive']`; + this.createWorkTitleHeaderText = `css=div[class='flex space-x-4']:nth-child(2) div[class='space-y-2 w-full'] label`; + this.createWorkTitleInput = `css=input[name='title']`; + this.createWorkTitleErrorText = `css=div[class='space-y-2 w-full'] p[class='text-sm font-medium text-destructive']`; + this.createWorkPublicationYearHeaderText = `css=div[class='flex space-x-4']:nth-child(3) div[class='space-y-2 w-1/3']:nth-child(1) label`; + this.createWorkPublicationYearInput = `css=input[name='publication_year']`; + this.createWorkPublicationMonthHeaderText = `css=div[class='flex space-x-4']:nth-child(3) div[class='space-y-2 w-1/3']:nth-child(2) label`; + this.createWorkPublicationMonthInput = `css=input[name='publication_month']`; + this.createWorkPublicationDayHeaderText = `css=div[class='flex space-x-4']:nth-child(3) div[class='space-y-2 w-1/3']:nth-child(3) label`; + this.createWorkPublicationDayInput = `css=input[name='publication_day']`; + + this.projectsHeaderText = `xpath=//h1[normalize-space()='Research Projects']`; + this.projectsPlusIconButton = `css=div[class="flex justify-between items-center mb-6"] a[href="/profile/funding/create"]`; + this.noProjectsIcon = `css=div[class='w-16 h-16 rounded-full bg-gray-100 flex items-center justify-center text-gray-400'] svg`; + this.noProjectsHeaderText = `css=h2[class='text-xl font-semibold']`; + this.noProjectsText = `css=p[class='text-gray-500 max-w-md break-normal']`; + this.noProjectsAddButton = `css=div[class='bg-white rounded-lg p-8 border border-gray-200 flex flex-col items-center justify-center text-center space-y-4'] a[href='/profile/funding/create']`; + + this.createProjectsHeaderTextSection = `css=div[class='flex justify-between items-center mb-6']`; + this.createProjectsTypeHeaderText = `css=div[class='flex space-x-4']:nth-child(1) div[class='space-y-2 w-1/3'] label`; + this.createProjectsTypeButtonText = `css=button[role='combobox']`; + this.createProjectsTitleHeaderText = `css=div[class='flex space-x-4']:nth-child(1) div[class='space-y-2 w-2/3'] label`; + this.createProjectsTitleInput = `css=input[name='title']`; + this.createProjectsOrgHeaderText = `css=div[class='flex space-x-4']:nth-child(2) div[class='space-y-2 w-1/4']:nth-child(1) label`; + this.createProjectsOrgInput = `css=input[name='organization']`; + this.createProjectsOrgTypeHeaderText = `css=div[class='flex space-x-4']:nth-child(2) div[class='space-y-2 w-1/4']:nth-child(2) label`; + this.createProjectsOrgTypeInput = `css=input[name='organization_defined_type']`; + this.createProjectsOrgLocationHeaderText = `css=div[class='flex space-x-4']:nth-child(2) div[class='space-y-2 w-1/4']:nth-child(3) label`; + this.createProjectsOrgLocationInput = `css=input[name='organization_address']`; + this.createProjectsOrgAmountHeaderText = `css=div[class='flex space-x-4']:nth-child(2) div[class='space-y-2 w-1/4']:nth-child(4) label`; + this.createProjectsOrgAmountInput = `css=input[name='amount']`; + this.createProjectsStartYearHeaderText = `css=div[class='flex space-x-4']:nth-child(3) div[class='space-y-2 w-1/6']:nth-child(1) label`; + this.createProjectsStartYearInput = `css=input[name='start_year']`; + this.createProjectsStartMonthHeaderText = `css=div[class='flex space-x-4']:nth-child(3) div[class='space-y-2 w-1/6']:nth-child(2) label`; + this.createProjectsStartMonthInput = `css=input[name='start_month']`; + this.createProjectsStartDayHeaderText = `css=div[class='flex space-x-4']:nth-child(3) div[class='space-y-2 w-1/6']:nth-child(3) label`; + this.createProjectsStartDayInput = `css=input[name='start_day']`; + this.createProjectsEndYearHeaderText = `css=div[class='flex space-x-4']:nth-child(3) div[class='space-y-2 w-1/6']:nth-child(4) label`; + this.createProjectsEndYearInput = `css=input[name='end_year']`; + this.createProjectsEndMonthHeaderText = `css=div[class='flex space-x-4']:nth-child(3) div[class='space-y-2 w-1/6']:nth-child(5) label`; + this.createProjectsEndMonthInput = `css=input[name='end_month']`; + this.createProjectsEndDayHeaderText = `css=div[class='flex space-x-4']:nth-child(3) div[class='space-y-2 w-1/6']:nth-child(6) label`; + this.createProjectsEndDayInput = `css=input[name='end_day']`; + this.selectProjectTypeComboBoxInput = `css=`; + this.selectProjectTypeComboBoxTextValues = `css=`; + + this.noDataFoundTexts = `css=div[class='bg-white rounded-lg p-8 border border-gray-200 flex flex-col items-center justify-center text-center space-y-4']`; + this.createTypeSearchInput = `css=div[class='flex items-center border-b px-3'] input`; + this.noResultsFoundMessage = `css=div[class='py-6 text-center text-sm']`; + + this.loadingRecordsText = `css=div[class='flex-1 bg-white rounded-lg border shadow-sm overflow-hidden'] div[class='p-6'] p`; + this.table = `css=div[class='border-gray-200']`; + this.tableUpdateButton = `css=tbody tr td:last-child div button:first-child`; + this.tableDeleteButton = `css=tbody tr td:last-child div button:last-child`; + this.deleteModalHeaderText = `css=h2[class='text-lg font-semibold']`; + this.deleteModalButton = `css=div[role='alertdialog'] button:last-child`; + + this.toastMessageHeaderText = `css=div[class='grid gap-1'] div[class='text-sm font-semibold']`; + this.toastMessageText = `css=div[class='grid gap-1'] div[class='text-sm opacity-90']`; + this.toastCloseButton = `css=button[class='absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600']`; + this.backAndCancelButton = `css=div[class='flex justify-between items-center mb-6'] a[class='inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border bg-background hover:bg-accent h-10 px-4 py-2 border-gray-200 text-gray-700 hover:text-gray-900 flex items-center gap-2']`; + this.submitButton = `css=button[type='submit']`; + + //Create Projects + this.selectProjectTypeComboBoxInput = `css=div[class='flex flex-col'] button[role="combobox"]`; + this.selectProjectTypeComboBoxTextValues = `css=div[role="option"]`; + } +} diff --git a/pageObjectModel/registerPage.ts b/pageObjectModel/registerPage.ts new file mode 100644 index 0000000..e069f16 --- /dev/null +++ b/pageObjectModel/registerPage.ts @@ -0,0 +1,56 @@ +import { Page } from "playwright"; + +export class registerPage { + readonly heroText: string; + readonly heroText2: string; + readonly firstName: string; + readonly lastName: string; + readonly email: string; + readonly phoneNumber: string; + readonly password: string; + readonly eyeIcon: string; + readonly confirmPassword: string; + readonly privacyPolicyText: string; + readonly privacyPolicyCheckbox: string; + readonly submitButton: string; + readonly toastMessage: string; + readonly firstNameHeader: string; + readonly firstNameErrorMessage: string; + readonly lastNameHeader: string; + readonly lastNameErrorMessage: string; + readonly emailInputBoxHeader: string; + readonly phoneNumberHeader: string; + readonly passwordInputBoxHeader: string; + readonly confirmPasswordHeader: string; + readonly emailInputBoxErrorMessage: string; + readonly phoneNumberInputBoxErrorMessage: string; + readonly passwordInputBoxErrorMessage: string; + readonly confirmPasswordInputBoxErrorMessage: string; + + constructor(page: Page) { + this.heroText = `xpath=//div[normalize-space()='To register, please fill in the form below.']`; + this.heroText2 = `css=div[class='flex flex-col space-y-1.5 p-6'] div:nth-child(3)`; + this.firstNameHeader = `css=label[for=':re:-form-item']`; + this.firstName = `css=input[id=':re:-form-item']`; + this.firstNameErrorMessage = `css=p[id=':re:-form-item-message']`; + this.lastNameHeader = `css=label[for=':rf:-form-item']`; + this.lastName = `css=input[id=':rf:-form-item']`; + this.lastNameErrorMessage = `css=p[id=':rf:-form-item-message']`; + this.emailInputBoxHeader = `css=label[for=':rg:-form-item']`; + this.email = `css=input[id=':rg:-form-item']`; + this.emailInputBoxErrorMessage = `css=p[id=':rg:-form-item-message']`; + this.phoneNumberHeader = `css=label[for=':rh:-form-item']`; + this.phoneNumber = `css=input[id=':rh:-form-item']`; + this.phoneNumberInputBoxErrorMessage = `css=p[id=':rh:-form-item-message']`; + this.passwordInputBoxHeader = `css=label[for=':r1g:-form-item']`; + this.password = `css=input[id=':ri:-form-item']`; + this.passwordInputBoxErrorMessage = `css=p[id=':r1g:-form-item-message']`; + this.confirmPasswordHeader = `css=label[for=':rj:-form-item']`; + this.confirmPassword = `css=input[id=':rj:-form-item']`; + this.confirmPasswordInputBoxErrorMessage = `css=p[id=':rj:-form-item-message']`; + this.privacyPolicyText = `css=div[class='flex items-center space-x-2']`; + this.privacyPolicyCheckbox = `css=div[class='flex items-center space-x-2'] button[role='checkbox']`; + this.submitButton = `css=button[type='submit']`; + this.toastMessage = `css=div[class='grid gap-1']`; + } +} diff --git a/pageObjectModel/startMatchingPage.ts b/pageObjectModel/startMatchingPage.ts new file mode 100644 index 0000000..c2449ee --- /dev/null +++ b/pageObjectModel/startMatchingPage.ts @@ -0,0 +1,9 @@ +import { Page } from "@playwright/test"; + +export class startMatchingPage { + readonly startMatchingHeroText: string; + + constructor(page: Page) { + this.startMatchingHeroText = `css=h2[class='text-xl text-center text-blue-600 font-bold']`; + } +} diff --git a/playwright.config.ts b/playwright.config.ts index fefd8b3..c109648 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,28 +1,45 @@ import { defineConfig, devices } from "@playwright/test"; +import fs from "fs"; +import * as dotenv from "dotenv"; + +dotenv.config(); + +// We're Deleting the last run reports to create a new one +const LAST_RUN_FILE = ".last-run.json"; +if (fs.existsSync(LAST_RUN_FILE)) { + fs.unlinkSync(LAST_RUN_FILE); +} export default defineConfig({ testDir: "./tests", + + // disabling the parallelism for the test suite to avoid flaky tests due to shared state fullyParallel: true, + + // Forbidding test.only on CI forbidOnly: !!process.env.CI, - retries: process.env.CI ? 2 : 0, - workers: process.env.CI ? 1 : undefined, + // Retry failed tests only on CI + retries: process.env.CI ? 1 : 0, - reporter: [ - ["html", { outputFolder: "reports/html" }], - // ["json", { outputFile: "reports/results.json" }], - // ["junit", { outputFile: "reports/results.xml" }], - ], + // Running all tests in a single worker + workers: 5, + + // Generating only HTML reporter/ Allure report will be in the test run in the upcoming time + reporter: [["allure-playwright", { resultsDir: "reports/allure-results" }]], use: { trace: "on-first-retry", + screenshot: "only-on-failure", }, + // Browser options for test execution + // Executing tests only in chrome headless mode now + // In the upcoming time, we will be adding more browsers and CI/CD pipeline projects: [ { name: "chromium", use: { ...devices["Desktop Chrome"] }, }, - ], }); diff --git a/pom/accountPage.ts b/pom/accountPage.ts deleted file mode 100644 index c23a6c9..0000000 --- a/pom/accountPage.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Page } from "playwright"; - -export class AccountPage { - readonly loginPageText: string; - readonly accountMenus: string; - - constructor(page: Page) { - this.loginPageText = `[class='m-0 font-size-sm'] strong`; - this.accountMenus = `.dropdown-menu.mz-sub-menu-96 > li > .both.dropdown-item.icon-left > .info > .title`; - } -} diff --git a/pom/lambdaHomePage.ts b/pom/lambdaHomePage.ts deleted file mode 100644 index fcd4cc8..0000000 --- a/pom/lambdaHomePage.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Page } from "playwright"; - -export class LambdaHomePage { - readonly textInputField: string; - readonly searchButton: string; - readonly accountButton: string; - readonly loginButton: string; - readonly emailField: string; - readonly passwordField: string; - readonly accountLoginButton: string; - readonly rightColumnList: string; - - constructor(page: Page) { - this.textInputField = `#entry_217820 [type='text']`; - this.searchButton = `div#search .type-text`; - this.accountButton = `div#widget-navbar-217834 > ul > li:nth-of-type(6) > a[role='button'] .title`; - this.loginButton = `div#widget-navbar-217834 .dropdown-menu.mz-sub-menu-96 > li:nth-of-type(1) > .both.dropdown-item.icon-left`; - this.emailField = `input#input-email`; - this.passwordField = `#input-password`; - this.accountLoginButton = `[action] .btn-primary`; - this.rightColumnList = `aside#column-right > div > a`; - } -} diff --git a/pom/loginPage.ts b/pom/loginPage.ts deleted file mode 100644 index b491645..0000000 --- a/pom/loginPage.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Page } from "playwright"; - -export class LoginPage { - readonly textInputField: string; - readonly emailInputField: string; - readonly passwordInputField: string; - readonly loginButton: string; - - - constructor(page: Page) { - this.textInputField = `#entry_217820 [type='text']`; - this.emailInputField = `#input-email`; - this.passwordInputField = `#input-password`; - this.loginButton = `[action] .btn-primary`; - - } -} diff --git a/testData/dashboardPageData.json b/testData/dashboardPageData.json new file mode 100644 index 0000000..86d19d8 --- /dev/null +++ b/testData/dashboardPageData.json @@ -0,0 +1,68 @@ +{ + "dashboardNavButtonText": "Dashboard", + "dashboardNavStartMatchingText": "Start Matching", + "dashboardNavMatchesText": "Matches", + "dashboardNavNotificationsText": "Notifications", + "dashboardNavAccountText": "Account", + "dashboardNavAccountProfileText": "Profile", + "dashboardNavAccountAdminText": "Admin", + "dashboardNavAccountSubmitFeedbackText": "Submit Feedback", + "dashboardNavAccountLogoutText": "Logout", + + "navbarTexts": ["Dashboard", "Start Matching", "Matches", "Notifications"], + "dashboardNavAccountTexts": ["Profile", "Admin", "Logout"], + + "dashboardHrefValue": "/dashboard", + "dashboardStartMatchingHrefValue": "/matcher", + "dashboardMatchesHrefValue": "/match", + "dashboardNotificationsHrefValue": "/notifications", + "dashboardAccountProfileHrefValue": "/profile", + "dashboardAccountAdminHrefValue": "/organization-admin", + "dashboardAccountLogoutHrefValue": "/logout", + + "dashboardAllCardHeaderTexts": [ + "# Opportunities", + "Opportunities", + "Funding Available", + "# Researchers", + "Funding Scenario Status\nView all", + "Recent Match Notifications\nView all" + ], + + "dashboardAllOpportunitiesCardText": "All FundFit opportunities", + "dashboardNewResearchersOpportunitiesCardText": "New in last 7 days", + "dashboardExpireResearchersOpportunitiesCardText": "Expiring in next 7 days", + "dashboardFoundingAvailableCardText": "My potential / Total funding", + "dashboardAllResearchersCardText": "Researchers available for matching", + "dashboardFoundingScenarioText": "Your funding scenario distribution", + "dashboardRecentMatchNotificationsText": "Your configured notification triggers", + + "dashboardFoundingScenarioButton": "View all", + "dashboardRecentMatchNotificationsButton": "View all", + + "dashboardNotificationViewButton": "View", + "dashboardNotificationMatchTexts": [ + "Match Opportunities", + "Match Researchers" + ], + "dashboardNotificationTexts": ["Researchers", "Opportunities"], + + "notificationModalHeader": "Notification", + "notificationModalHeader2": "Bulk Notification", + "notificationModalNameLabel": "Name", + "notificationModalNotificationTypeSelectLabel": "Notification Type", + "notificationModalNotificationTypeSelectOption": ["Email", "RSS"], + "notificationModalUsersEmailSelectLabel": "Select Email Users", + "notificationModalEmailCheckboxLabel": "Confirm receiving emails?", + "notificationModalEmailCheckboxText": "You will receive daily emails with opportunities that match your search", + "notificationModalMatchTypeSelectLabel": "Match Type", + "notificationModalOpportunitiesSelectLabel": "Opportunity Search", + "notificationModalSelectCollaborationLabel": "Select Collaboration Institutions", + "notificationModalSelectCollaborationText": "If you leave this open, our AI will attempt to match you across all researchers!", + "notificationModalFindCollaborationHeader": "Find Collaboration Institutions", + "notificationModalFindCollaborationLabel": "Select Institutions", + "notificationModalFindCollaborationText": "Selected 1 institution", + "notificationModalCloseButton": "Close", + + "noNotificationText": "No notifications set up yetCreate Notification" +} diff --git a/testData/fundFit.json b/testData/fundFit.json new file mode 100644 index 0000000..e299659 --- /dev/null +++ b/testData/fundFit.json @@ -0,0 +1,17 @@ +{ + "textDecorationColorCss": "text-decoration-color", + "textColorCss": "color", + "backgroundColorCss": "background-color", + "hrefCss": "href", + "fundFitTitle": "FundFit", + "logoImageSource": "/assets/logo-BTFpVDFz.png", + "blackColorCode": "rgb(0, 0, 0)", + "secondBlackColorCode": "rgb(2, 8, 23)", + "grayColorCode": "rgb(100, 116, 139)", + "redColorCode": "rgb(217, 38, 38)", + "red500ColorCode": "rgb(239, 68, 68)", + "orangeColorCode": "rgb(255, 109, 0)", + "secondOrangeColor": "rgb(255, 255, 255)", + "blueColorCode": "rgb(37, 99, 235)", + "submitButtonText": "Submit" +} diff --git a/testData/globalData.json b/testData/globalData.json deleted file mode 100644 index 7a0441f..0000000 --- a/testData/globalData.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "landingPageUrl": "https://playwright.dev/", - "landingPageTitle":"Fast and reliable end-to-end testing for modern web apps | Playwright" -} \ No newline at end of file diff --git a/testData/homePageData.json b/testData/homePageData.json new file mode 100644 index 0000000..1bc0824 --- /dev/null +++ b/testData/homePageData.json @@ -0,0 +1,21 @@ +{ + "SuccessfulLoginToastText": "Login SuccessfulYou have been logged in successfully.", + "textDecorationColorCss": "text-decoration-color", + "fundFitTitle": "FundFit", + "logoImageSource": "/assets/logo-BTFpVDFz.png", + "blackColorCode": "rgb(0, 0, 0)", + "loginButtonText": "Login", + "emailInputBoxPlaceholderText": "Enter your email", + "homePageHeroText": "Better funding + teaming with AI insights", + "homePageHeroText2": "Learn why institutions across the country are adopting Streamlyne FundFit to locate funding opportunities and build best-fit research teams faster than ever", + "registerButtonText": "Register", + "homePageViewPricingButtonText": "View pricing", + "homePageStartTrialButtonText": "Start free trial", + "homePageFooterText": "© 2025 Streamlyne. All Rights Reserved. Version: latest", + "userTierHeroText": "Simple, transparent pricing", + "userTierSubHeroText": "Choose the plan that works best for you or your organization", + "researcherSectionHeaderText": "I'm a Researcher...", + "administratorSectionHeaderText": "I'm an Administrator...", + "loginButtonHrefValue": "/login", + "registerButtonHrefValue": "/register" +} diff --git a/testData/lambda.json b/testData/lambda.json deleted file mode 100644 index 5997dbc..0000000 --- a/testData/lambda.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "lambdaTestUrl": "https://ecommerce-playground.lambdatest.io/", - "loginPageUrl": "https://ecommerce-playground.lambdatest.io/index.php?route=account/login", - "accountPageUrl": "https://ecommerce-playground.lambdatest.io/index.php?route=account/account", - "pageTitle": "Your Store", - "expectedTexts": [ - " Login", - " Register", - " Forgotten Password", - " My Account", - " Address Book", - " Wish List", - " Order History", - " Downloads", - " Recurring payments", - " Reward Points", - " Returns", - " Transactions", - " Newsletter" - ], - "accountMenuTexts": [ - " Dashboard", - " My order", - " Return", - " Tracking", - " My voucher", - " Logout" - ] -} diff --git a/testData/loginPageData.json b/testData/loginPageData.json new file mode 100644 index 0000000..f75a653 --- /dev/null +++ b/testData/loginPageData.json @@ -0,0 +1,16 @@ +{ + "SuccessfulLoginToastText": "Login SuccessfulYou have been logged in successfully.", + "loginButtonText": "Login", + "invalidEmailOrPasswordErrorMessage": "ErrorLogin failed. Please try again.", + "loginPageEmailInputBoxHeader": "Email Address", + "loginPagePasswordInputBoxHeader": "Password", + "loginPageEmailPlaceholderText": "Enter your email", + "loginPagePasswordPlaceholderText": "Enter your password", + "loginPageEmailErrorMessage": "Invalid email address", + "loginPagePasswordErrorMessage": "Password is required", + "forgetPasswordButtonText": "Forgot password?", + "forgetPasswordHrefValue": "/forgot-password", + "forgetPasswordHeaderText": "To reset your password, please enter your email address.", + "submitButtonText": "Submit", + "forgetPasswordSuccessfulRequestText": "Forgot PasswordWe have sent you an email to set your password." +} diff --git a/testData/profilePageData.json b/testData/profilePageData.json new file mode 100644 index 0000000..6d9f09f --- /dev/null +++ b/testData/profilePageData.json @@ -0,0 +1,171 @@ +{ + "profileTitle": "Your Public Profile", + "sidebarGeneralInfoButtonText": "General Information", + "sidebarBackgroundButtonText": "Background", + "sidebarWorksButtonText": "Works", + "sidebarProjectsButtonText": "Projects", + "sidebarAttachmentsButtonText": "Attachments", + + "sidebarButtonTexts": [ + "General Information", + "Background", + "Works", + "Projects", + "Attachments" + ], + "sidebarORCIDButtonText": "Disconnect from ORCID", + + "topBarButtonTexts": [ + "General", + "Background", + "Works", + "Projects", + "Attachments" + ], + + "profileHeaderText": "General Information", + "EditProfileButtonText": "Edit Profile", + "passwordSettingsButtonText": "Password Settings", + "emailSettingsButtonText": "Email Settings", + + "firstNameHeaderText": "First Name", + "lastNameHeaderText": "Last Name", + "emailHeaderText": "Email", + "institutionHeaderText": "Institution", + "phoneNumberHeaderText": "Phone Number", + "biographyHeaderText": "Biography", + + "editProfileHeaderText": "Update Profile", + "editProfileFirstNameHeaderText": "First Name*", + "editProfileFirstNameErrorText": "First name is required", + "editProfileFirstNameErrorText2": "First Name should only contain letters, spaces, hyphens, and apostrophes.", + "editProfileLastNameHeaderText": "Last Name*", + "editProfileLastNameErrorText": "Last name is required", + "editProfileLastNameErrorText2": "Last Name should only contain letters, spaces, hyphens, and apostrophes.", + "editProfileFirstNameLastNameErrorText": "Max length is 50 characters", + "editProfilePhoneNumberHeaderText": "Phone Number", + "editProfilePhoneNumberPlaceholderText": "Please enter your phone number", + "editProfileNoValueProviderText": "Not Provided", + "editProfilePhoneNumberErrorText": "Phone number must contain only numbers", + "editProfilePhoneNumberErrorText2": "Ensure this field has no more than 15 characters.", + "editProfileBiographyPlaceholderText": "Biography", + + "passwordSettingsHeaderText": "Change Password", + "passwordSettingsNewPasswordHeaderText": "New Password*", + "passwordSettingsNewPasswordPlaceholderText": "Enter your new password", + "passwordSettingsConfirmNewPasswordHeaderText": "Confirm New Password*", + "passwordSettingsConfirmNewPasswordPlaceholderText": "Confirm your new password", + "passwordSettingsConfirmNewPasswordErrorText": "The two password fields didn’t match.", + "passwordSettingsConfirmNewPasswordErrorText2": "You have used this password before. Please choose a new one.", + + "emailSettingsHeaderText": "Email Settings", + "emailSettingsCurrentEmailHeaderText": "Current Email", + "emailSettingsEditEmailButtonText": "Edit Email", + "emailSettingsEditEmailHeaderText": "Edit Email", + "emailSettingsNewEmailHeaderText": "New Email*", + "emailSettingsAlreadyVerifiedErrorText": "Email is already verified.", + "emailSettingsInvalidErrorText": "Invalid email address", + "emailSettingsRequiredErrorText": "Email is required", + + "successfullyProfileUpdateToast": "Profile UpdatedYour profile has been updated successfully.", + + "backgroundHeaderText": "Background", + "noBackgroundHeaderText": "No background information", + "noBackgroundText": "Add your education and work experience to improve your matching results.", + "noBackgroundAddButtonText": "Add Background", + + "createBackgroundHeaderText": "Create Background", + "createBackgroundTypeHeaderText": "Background Type*", + "createBackgroundTypeButtonText": "Select background type...", + "createBackgroundTypeErrorText": "Background Type is required", + "successfullyBackgroundCreateToast": "Background CreatedThe background has been successfully created.", + "successfullyBackgroundUpdateToast": "Background UpdatedThe background has been successfully updated.", + "successfullyBackgroundDeleteToast": "Background DeletedThe background has been successfully deleted.", + + "worksHeaderText": "Research Works", + "noWorksText": "No research worksAdd your research works to improve your matching results.Add Work", + + "createWorkHeaderText": "Create Research Work", + "createWorkTypeHeaderText": "Work Type*", + "createWorkTypeButtonText": "Select work type...", + "createWorkTypeSearchPlaceholderText": "select work type...", + "createWorkTypeErrorText": "Work Type is required", + "createWorkTypeDropdownTexts": [ + "Annotation", + "Artistic Performance", + "Conference Abstract", + "Conference Paper", + "Conference Poster", + "Data Management Plan", + "Data Set", + "Dictionary Entry", + "Disclosure", + "Dissertation/Thesis", + "Edited Book", + "Encyclopedia Entry", + "Invention", + "Book", + "Book Chapter", + "Book Review", + "Edited Book", + "Encyclopedia Entry", + "Invention", + "Journal Article", + "Journal Issue", + "Lecture/Speech", + "License", + "Magazine Article", + "Manual", + "Newsletter Article", + "Newspaper Article", + "Online Resource", + "Other", + "Patent", + "Physical Object", + "Preprint", + "Registered Copyright", + "Report", + "Research Technique", + "Research Tool", + "Review", + "Software", + "Spin-off Company", + "Standards and Policy", + "Supervised Student Publication", + "Technical Standard", + "Test", + "Trademark", + "Translation", + "Website", + "Working Paper" + ], + "createWorkJournalTitleHeaderText": "Journal Title*", + "createWorkJournalTitleErrorText": "Journal Title is required", + "createWorkTitleHeaderText": "Title*", + "createWorkTitleErrorText": "Title is required", + "createWorkPublicationYearHeaderText": "Publication Year", + "createWorkPublicationMonthHeaderText": "Publication Month", + "createWorkPublicationDayHeaderText": "Publication Day", + "successfullyWorkCreateToast": "Research Work CreatedThe research work has been successfully created.", + "successfullyWorkUpdateToast": "Research Work UpdatedThe research work has been successfully updated.", + "successfullyWorkDeleteToast": "Research Work DeletedThe research work has been successfully deleted.", + + "projectHeaderText": "Research Projects", + "noProjectsHeaderText": "No research projects", + "noProjectsText": "No research projectsAdd your research projects and funding information to improve your matching results.Add Project", + "noProjectsAddButtonText": "Add Project", + + "noResultsFoundMessage": "No results found.", + "deleteModalHeaderText": "Are you sure you want to delete?", + "deleteModalDeleteButtonText": "Delete", + + "cancelButtonText": "Cancel", + "submitButtonText": "Submit", + "userFirstName": "Md Mahbubur", + "userLastName": "Rahman", + + "createProjectFormHeaderText": "Create Project", + "backButtonText": "Back", + + "comboBoxPlaceholderText": ["Contract", "Salary Award", "Grant", "Award"] +} diff --git a/testData/registerPageData.json b/testData/registerPageData.json new file mode 100644 index 0000000..2e4e3c3 --- /dev/null +++ b/testData/registerPageData.json @@ -0,0 +1,30 @@ +{ + "registerPageHeroText": "To register, please fill in the form below.", + "registerPageHeroText2": "No FundFit account? Request a Demo", + "registerPageFirstNamePlaceholderText": "Please enter your first name", + "registerPageLastNamePlaceholderText": "Please enter your last name", + "registerPageEmailPlaceholderText": "Enter your email", + "registerPagePhonePlaceholderText": "Please enter your phone number", + "registerPagePasswordPlaceholderText": "Enter your password", + "registerPageConfirmPasswordPlaceholderText": "Confirm your password", + "registerPagePrivacyPolicyText": "By registering, I agree to the Terms of Service and Privacy Policy", + "firstNameHeaderText": "First Name", + "lastNameHeaderText": "Last Name", + "emailHeaderText": "Email", + "phoneHeaderText": "Phone", + "passwordHeaderText": "Password", + "confirmPasswordHeaderText": "Confirm Password", + "registerButtonText": "Register", + "registerPageFirstNameErrorText": "First Name is required", + "registerPageLastNameErrorText": "Last Name is required", + "registerPageEmailErrorText": "Email is required", + "registerPagePhoneErrorText": "Phone number is required", + "registerPagePasswordErrorText": "Password is required", + "registerPageConfirmPasswordErrorText": "Confirm password is required", + "registerPagePasswordMismatchErrorText": "Passwords do not match", + "registerPageInvalidEmailErrorText": "Invalid email address", + "registerPageInvalidPhoneErrorText": "Please enter a valid phone number", + "registerPageInvalidPasswordErrorText": "Password must be at least 8 characters", + "registerPageInvalidPhoneNumberErrorText": "Phone number must contain only numbers", + "registerPageRegistrationSuccessMessage": "Registration SuccessfulPlease check your email for verification." +} diff --git a/testData/text.json b/testData/text.json new file mode 100644 index 0000000..18c390e --- /dev/null +++ b/testData/text.json @@ -0,0 +1,20 @@ +{ + "expectedTexts": [ + "Full access to all features", + "Unlimited match notifications", + "Unlimited opportunities", + "Match against 250,000+ Researchers", + "Priority support", + "Mobile app access", + "Setup Mobile Notifications", + "Full access to all features", + "Unlimited match notifications", + "Unlimited opportunities", + "Match against 250,000+ Researchers", + "Priority support", + "Mobile app access", + "Setup MS Teams/Slack integration", + "Assemble teams of Researchers", + "SAML2 Single Sign-On Integration" + ] +} diff --git a/tests/HomePageTest.spec.ts b/tests/HomePageTest.spec.ts new file mode 100644 index 0000000..9ba37c6 --- /dev/null +++ b/tests/HomePageTest.spec.ts @@ -0,0 +1,379 @@ +import { test } from "../utilities/fixtures"; +import { ExpectedTextProvider } from "../utilities/valueProvider"; +import { ENV } from "../utilities/env"; +import { FakerDataProvider } from "../utilities/fakerDataProvider"; +import fundFitData from "../testData/fundFit.json"; +import registerPageData from "../testData/registerPageData.json"; +import homePageData from "../testData/homePageData.json"; +import loginPageData from "../testData/loginPageData.json"; + +class HomePageTest extends ExpectedTextProvider { + private fakerData: FakerDataProvider; + constructor() { + super(); + this.fakerData = new FakerDataProvider(); + } + + runTests() { + const { firstName, lastName, email, phoneNumber, password } = + this.fakerData; + + test.describe("Validating User Landing Page Scenarios", () => { + test.beforeEach(async ({ runner, userLandingPage }) => { + await runner.navigateTo(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyContainsUrl(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyPageTitle(fundFitData.fundFitTitle); + // await runner.waitForMilliseconds(10000) + // await runner.validateAttribute( + // userLandingPage.headerImage, + // "src", + // fundFitData.logoImageSource, + // ); + }); + + test("Validating Successful Navigation To Login Page", async ({ + runner, + userLandingPage, + userLoginPage, + }) => { + await runner.verifyElementIsVisible(userLandingPage.loginButton); + await runner.verifyElementToHaveCSSProperty( + userLandingPage.loginButton, + fundFitData.textColorCss, + fundFitData.blackColorCode, + ); + await runner.verifyElementText( + userLandingPage.loginButton, + loginPageData.loginButtonText, + ); + await runner.validateTextAndClickOnElement( + userLandingPage.loginButton, + homePageData.loginButtonText, + ); + await runner.waitUntilElementIsInvisible( + userLandingPage.startTrialButton, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_LOGIN_PAGE_URL); + await runner.verifyElementIsVisible(userLoginPage.email); + + await runner.verifyElementPlaceholder( + userLoginPage.email, + homePageData.emailInputBoxPlaceholderText, + ); + await runner.clickOnElement(userLandingPage.headerImage); + await runner.verifyContainsUrl(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyElementText( + userLandingPage.homePageHeroText, + homePageData.homePageHeroText, + ); + }); + + test("Validating Successful Navigation To Register Page", async ({ + runner, + userLandingPage, + userRegisterPage, + }) => { + await runner.verifyElementIsVisible(userLandingPage.registerButton); + await runner.validateTextAndClickOnElement( + userLandingPage.registerButton, + homePageData.registerButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_REGISTER_PAGE_URL); + await runner.verifyElementText( + userRegisterPage.heroText, + registerPageData.registerPageHeroText, + ); + await runner.verifyElementIsVisible(userRegisterPage.heroText); + + await runner.verifyElementPlaceholder( + userRegisterPage.firstName, + registerPageData.registerPageFirstNamePlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.firstName); + await runner.enterText(userRegisterPage.firstName, firstName); + await runner.verifyInputValue(userRegisterPage.firstName, firstName); + + await runner.verifyElementPlaceholder( + userRegisterPage.lastName, + registerPageData.registerPageLastNamePlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.lastName); + await runner.enterText(userRegisterPage.lastName, lastName); + await runner.verifyInputValue(userRegisterPage.lastName, lastName); + + await runner.verifyElementPlaceholder( + userRegisterPage.email, + registerPageData.registerPageEmailPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.email); + await runner.enterText(userRegisterPage.email, email); + await runner.verifyInputValue(userRegisterPage.email, email); + + await runner.verifyElementPlaceholder( + userRegisterPage.phoneNumber, + registerPageData.registerPagePhonePlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.phoneNumber); + await runner.enterText(userRegisterPage.phoneNumber, phoneNumber); + await runner.verifyInputValue( + userRegisterPage.phoneNumber, + phoneNumber, + ); + + await runner.verifyElementPlaceholder( + userRegisterPage.password, + registerPageData.registerPagePasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.password); + await runner.enterText(userRegisterPage.password, password); + await runner.verifyInputValue(userRegisterPage.password, password); + + await runner.verifyElementPlaceholder( + userRegisterPage.confirmPassword, + registerPageData.registerPageConfirmPasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.confirmPassword); + await runner.enterText(userRegisterPage.confirmPassword, password); + await runner.verifyInputValue( + userRegisterPage.confirmPassword, + password, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.privacyPolicyCheckbox, + ); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "unchecked", + ); + await runner.clickOnElement(userRegisterPage.privacyPolicyCheckbox); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "checked", + ); + + await runner.verifyElementIsVisible(userRegisterPage.submitButton); + await runner.verifyElementText(userRegisterPage.submitButton, "Submit"); + await runner.verifyElementIsVisible(userRegisterPage.submitButton); + + await runner.clickOnElement(userLandingPage.headerImage); + await runner.verifyContainsUrl(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyElementText( + userLandingPage.homePageHeroText, + homePageData.homePageHeroText, + ); + }); + + test("Validating The Set Free Trial Button & Navigation", async ({ + runner, + userLandingPage, + }) => { + await runner.verifyElementIsVisible(userLandingPage.startTrialButton); + await runner.validateTextAndClickOnElement( + userLandingPage.startTrialButton, + homePageData.homePageStartTrialButtonText, + ); + await runner.waitUntilElementIsInvisible( + userLandingPage.startTrialButton, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_REGISTER_PAGE_URL); + await runner.verifyElementIsVisible(userLandingPage.headerImage); + await runner.clickOnElement(userLandingPage.headerImage); + await runner.verifyElementIsVisible(userLandingPage.homePageHeroText); + await runner.verifyContainsUrl(ENV.FUND_FIT_TEST_ENV_URL); + }); + + test('Validating The "View Pricing Button & Navigation"', async ({ + runner, + userLandingPage, + }) => { + await runner.clickOnElement(userLandingPage.viewPricingButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyElementIsScrolledIntoView( + userLandingPage.pricingSectionHeroText, + ); + await runner.verifyElementIsVisible( + userLandingPage.pricingSectionHeroText, + ); + }); + + test("Validating Pricing Button and Scrolling", async ({ + runner, + userLandingPage, + }) => { + await runner.verifyElementIsVisible(userLandingPage.viewPricingButton); + await runner.validateTextAndClickOnElement( + userLandingPage.viewPricingButton, + homePageData.homePageViewPricingButtonText, + ); + await runner.verifyElementIsVisible( + userLandingPage.researcherPriceText, + ); + await runner.verifyElementText( + userLandingPage.pricingSectionHeroText, + "Simple, transparent pricing", + ); + + await runner.verifyElementIsVisible( + userLandingPage.researcherPriceText, + ); + await runner.verifyElementIsVisible( + userLandingPage.researcherPriceText, + ); + }); + + // Flaky Test + test("Validating Footer Block of The Landing Page", async ({ + runner, + userLandingPage, + }) => { + await runner.verifyElementIsVisible(userLandingPage.headerImage); + await runner.scrollToElement(userLandingPage.footerBlock); + await runner.verifyElementIsVisible(userLandingPage.footerBlock); + await runner.verifyElementIsScrolledIntoView( + userLandingPage.footerBlock, + ); + await runner.verifyElementTextContains( + userLandingPage.footerBlock, + homePageData.homePageFooterText, + ); + }); + + test("Validating Platform User Tier Details", async ({ + runner, + userLandingPage, + }) => { + await runner.verifyElementIsVisible(userLandingPage.homePageHeroText); + await runner.verifyElementText( + userLandingPage.homePageHeroText, + homePageData.homePageHeroText, + ); + + await runner.verifyElementIsVisible(userLandingPage.homePageHeroText); + await runner.verifyElementText( + userLandingPage.homePageHeroText, + homePageData.homePageHeroText, + ); + await runner.verifyElementText( + userLandingPage.homePageSubHeroText, + homePageData.homePageHeroText2, + ); + + await runner.verifyElementIsVisible(userLandingPage.startTrialButton); + await runner.verifyElementText( + userLandingPage.startTrialButton, + homePageData.homePageStartTrialButtonText, + ); + + await runner.verifyElementIsVisible(userLandingPage.viewPricingButton); + await runner.verifyElementText( + userLandingPage.viewPricingButton, + homePageData.homePageViewPricingButtonText, + ); + + await runner.scrollToElement(userLandingPage.pricingSectionHeroText); + await runner.verifyElementIsVisible( + userLandingPage.pricingSectionHeroText, + ); + await runner.verifyElementText( + userLandingPage.pricingSectionHeroText, + homePageData.userTierHeroText, + ); + + await runner.verifyElementIsVisible( + userLandingPage.pricingSectionSubHeroText, + ); + + await runner.verifyElementText( + userLandingPage.pricingSectionSubHeroText, + homePageData.userTierSubHeroText, + ); + }); + + test("Validating the pricing section", async ({ + runner, + userLandingPage, + userRegisterPage, + }) => { + await runner.verifyElementIsVisible(userLandingPage.homePageHeroText); + await runner.scrollToElement(userLandingPage.pricingSectionSubHeroText); + await runner.verifyElementIsVisible( + userLandingPage.researcherBlockHeader, + ); + await runner.verifyElementText( + userLandingPage.researcherBlockHeader, + homePageData.researcherSectionHeaderText, + ); + + await runner.verifyElementIsVisible( + userLandingPage.administratorBlockHeader, + ); + await runner.verifyElementText( + userLandingPage.administratorBlockHeader, + homePageData.administratorSectionHeaderText, + ); + + await runner.verifyLinksTexts( + userLandingPage.pricingSectionLinkTexts, + this.expectedTexts, + ); + + await runner.verifyElementIsVisible(userLandingPage.getStartedButton); + await runner.validateTextAndClickOnElement( + userLandingPage.getStartedButton, + "Get Started", + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_REGISTER_PAGE_URL); + await runner.verifyElementIsVisible(userRegisterPage.heroText); + await runner.verifyElementText( + userRegisterPage.heroText, + registerPageData.registerPageHeroText, + ); + + await runner.verifyElementIsVisible(userRegisterPage.confirmPassword); + + await runner.verifyElementIsVisible(userLandingPage.headerImage); + await runner.clickOnElement(userLandingPage.headerImage); + + await runner.verifyContainsUrl(ENV.FUND_FIT_TEST_ENV_URL); + + await runner.verifyElementIsVisible(userLandingPage.contactSalesButton); + await runner.validateAttribute( + userLandingPage.contactSalesButton, + "href", + ENV.FUND_FIT_LANDING_PAGE_URL, + ); + await runner.verifyElementToHaveCSSProperty( + userLandingPage.contactSalesButton, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.scrollToElement(userLandingPage.footerBlock); + await runner.verifyElementIsVisible(userLandingPage.footerBlock); + await runner.verifyElementIsScrolledIntoView( + userLandingPage.footerBlock, + ); + + await runner.verifyElementToHaveCSSProperty( + userLandingPage.footerBlock, + "width", + "auto", + ); + await runner.verifyElementToHaveCSSProperty( + userLandingPage.footerBlock, + "height", + "auto", + ); + + await runner.verifyElementToHaveCSSProperty( + userLandingPage.footerBlock, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + }); + }); // Describe block + } // End of runTests method +} // End of HomePageTest class +const testSuite = new HomePageTest(); +testSuite.runTests(); diff --git a/tests/UserDashboardPageTest.spec.ts b/tests/UserDashboardPageTest.spec.ts new file mode 100644 index 0000000..c62b8c8 --- /dev/null +++ b/tests/UserDashboardPageTest.spec.ts @@ -0,0 +1,537 @@ +import { test } from "../utilities/fixtures"; +import { ExpectedTextProvider } from "../utilities/valueProvider"; +import { ENV } from "../utilities/env"; +import fundFitData from "../testData/fundFit.json"; +import dashboardPageData from "../testData/dashboardPageData.json"; +import loginPageData from "../testData/loginPageData.json"; + +class UserDashboardPageTest extends ExpectedTextProvider { + constructor() { + super(); + } + + runTests() { + test.describe("Validating Dashboard Page Test", () => { + test.beforeEach( + async ({ + runner, + userLandingPage, + userLoginPage, + userDashboardPage, + }) => { + await runner.navigateTo(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyContainsUrl(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyPageTitle(fundFitData.fundFitTitle); + // await runner.waitForMilliseconds(10000); + // await runner.validateAttribute( + // userLandingPage.headerImage, + // "src", + // fundFitData.logoImageSource, + // ); + + await runner.verifyElementIsVisible(userLandingPage.loginButton); + await runner.validateTextAndClickOnElement( + userLandingPage.loginButton, + loginPageData.loginButtonText, + ); + + await runner.verifyElementIsVisible(userLoginPage.email); + await runner.enterText(userLoginPage.email, ENV.FUND_FIT_USER_EMAIL); + + await runner.verifyElementIsVisible(userLoginPage.password); + await runner.enterText( + userLoginPage.password, + ENV.FUND_FIT_USER_PASSWORD, + ); + await runner.verifyElementIsVisible(userLoginPage.eyeIcon); + await runner.clickOnElement(userLoginPage.eyeIcon); + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.validateTextAndClickOnElement( + userLoginPage.loginButton, + loginPageData.loginButtonText, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + loginPageData.SuccessfulLoginToastText, + ); + + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + loginPageData.SuccessfulLoginToastText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_DASHBOARD_PAGE_URL); + await runner.verifyElementIsVisible( + userDashboardPage.dashboardButton, + ); + await runner.verifyElementText( + userDashboardPage.dashboardButton, + dashboardPageData.dashboardNavButtonText, + ); + await runner.verifyElementToHaveCSSProperty( + userDashboardPage.dashboardButton, + fundFitData.textDecorationColorCss, + fundFitData.orangeColorCode, + ); + }, + ); + + test("Validating Dashboard Navigation Bar", async ({ + runner, + userDashboardPage, + }) => { + await runner.verifyElementIsVisible(userDashboardPage.dashboardButton); + await runner.verifyElementToHaveCSSProperty( + userDashboardPage.dashboardButton, + fundFitData.textDecorationColorCss, + fundFitData.orangeColorCode, + ); + await runner.validateAttribute( + userDashboardPage.dashboardButton, + "href", + dashboardPageData.dashboardHrefValue, + ); + await runner.validateAttribute( + userDashboardPage.dashboardNavbarStartMatchingButton, + "href", + dashboardPageData.dashboardStartMatchingHrefValue, + ); + await runner.validateAttribute( + userDashboardPage.dashboardNavbarMatchesButton, + "href", + dashboardPageData.dashboardMatchesHrefValue, + ); + await runner.validateAttribute( + userDashboardPage.dashboardNavbarNotificationsButton, + "href", + dashboardPageData.dashboardNotificationsHrefValue, + ); + + await runner.verifyLinksTexts( + userDashboardPage.dashboardNavbarText, + dashboardPageData.navbarTexts, + ); + + await runner.verifyElementText( + userDashboardPage.dashboardNavbarAccountButton, + dashboardPageData.dashboardNavAccountText, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardNavbarAccountButton, + ); + await runner.verifyElementToHaveCSSProperty( + userDashboardPage.dashboardNavbarAccountButton, + fundFitData.textDecorationColorCss, + fundFitData.secondBlackColorCode, + ); + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardNavbarAccountButton, + dashboardPageData.dashboardNavAccountText, + ); + await runner.verifyElementIsVisible( + userDashboardPage.dashboardNavbarAccountProfileText, + ); + await runner.verifyElementText( + userDashboardPage.dashboardNavbarAccountProfileText, + dashboardPageData.dashboardNavAccountProfileText, + ); + await runner.validateAttribute( + userDashboardPage.dashboardNavbarAccountProfileText, + "href", + dashboardPageData.dashboardAccountProfileHrefValue, + ); + }); + + test("Validating Page Navigation From Dashboard", async ({ + runner, + userDashboardPage, + userStartMatchingPage, + userMatchesPage, + userNotificationPage, + }) => { + await runner.verifyElementIsVisible(userDashboardPage.dashboardButton); + await runner.verifyElementToHaveCSSProperty( + userDashboardPage.dashboardButton, + fundFitData.textDecorationColorCss, + fundFitData.orangeColorCode, + ); + + await runner.verifyElementToHaveCSSProperty( + userDashboardPage.dashboardNavbarMatchesButton, + fundFitData.textDecorationColorCss, + fundFitData.blackColorCode, + ); + + // Navigate Start Matching + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardNavbarStartMatchingButton, + dashboardPageData.dashboardNavStartMatchingText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_START_MATCHING_PAGE_URL); + await runner.verifyElementIsVisible( + userStartMatchingPage.startMatchingHeroText, + ); + + // Navigation Bar css validation problem + await runner.verifyElementToHaveCSSProperty( + userDashboardPage.dashboardNavbarStartMatchingButton, + fundFitData.textColorCss, + "rgb(116, 123, 255)", + ); + + await runner.verifyElementToHaveCSSProperty( + userDashboardPage.dashboardButton, + fundFitData.textDecorationColorCss, + fundFitData.blackColorCode, + ); + + // Navigate Matches + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardNavbarMatchesButton, + dashboardPageData.dashboardNavMatchesText, + ); + + await runner.verifyContainsUrl(ENV.FUND_FIT_MATCHES_PAGE_URL); + await runner.verifyElementIsVisible(userMatchesPage.matchesHeroText); + + // Navigate Notifications + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardNavbarNotificationsButton, + dashboardPageData.dashboardNavNotificationsText, + ); + + await runner.verifyContainsUrl(ENV.FUND_FIT_NOTIFICATIONS_PAGE_URL); + await runner.verifyElementIsVisible( + userNotificationPage.notificationHeroText, + ); + + // Account Menu Options + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardNavbarAccountButton, + dashboardPageData.dashboardNavAccountText, + ); + + await runner.verifyLinksTexts( + userDashboardPage.dashboardNavbarAccountTexts, + dashboardPageData.dashboardNavAccountTexts, + ); + + await runner.verifyElementText( + userDashboardPage.dashboardNavbarAccountSubmitFeedbackText, + "Submit Feedback", + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardNavbarAccountLogoutText, + ); + await runner.clickOnElement( + userDashboardPage.dashboardNavbarAccountLogoutText, + ); + }); + + test("Validating Dashboard All Card Headers", async ({ + runner, + userDashboardPage, + }) => { + await runner.verifyElementIsVisible( + userDashboardPage.dashboardAllOpportunitiesCard, + ); + await runner.verifyLinksTexts( + userDashboardPage.dashboardAllCardHeaderText, + dashboardPageData.dashboardAllCardHeaderTexts, + ); + }); + + test("Validating Dashboard All Opportunities Card", async ({ + runner, + userDashboardPage, + }) => { + await runner.verifyElementIsVisible( + userDashboardPage.dashboardAllOpportunitiesCardText, + ); + await runner.verifyElementText( + userDashboardPage.dashboardAllOpportunitiesCardText, + dashboardPageData.dashboardAllOpportunitiesCardText, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardAllOpportunitiesIcon, + ); + + await runner.waitUntilElementIsInvisible( + userDashboardPage.dashboardCardLoader, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardAllOpportunitiesValue, + ); + await runner.validateNumericValueFromElement( + userDashboardPage.dashboardAllOpportunitiesValue, + ); + }); + + test("Validating Dashboard Researchers Opportunities Card", async ({ + runner, + userDashboardPage, + }) => { + await runner.verifyElementIsVisible( + userDashboardPage.dashboardNewResearchersOpportunitiesCardText, + ); + await runner.verifyElementText( + userDashboardPage.dashboardNewResearchersOpportunitiesCardText, + dashboardPageData.dashboardNewResearchersOpportunitiesCardText, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardExpireResearchersOpportunitiesCardText, + ); + await runner.verifyElementText( + userDashboardPage.dashboardExpireResearchersOpportunitiesCardText, + dashboardPageData.dashboardExpireResearchersOpportunitiesCardText, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardResearchersOpportunitiesIcon, + ); + + await runner.waitUntilElementIsInvisible( + userDashboardPage.dashboardCardLoader, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardNewResearchersOpportunitiesValue, + ); + await runner.validateNumericValueFromElement( + userDashboardPage.dashboardNewResearchersOpportunitiesValue, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardExpireResearchersOpportunitiesValue, + ); + await runner.validateNumericValueFromElement( + userDashboardPage.dashboardExpireResearchersOpportunitiesValue, + ); + }); + + test("Validating Dashboard Founding Available Card", async ({ + runner, + userDashboardPage, + }) => { + await runner.verifyElementIsVisible( + userDashboardPage.dashboardFoundingAvailableCardText, + ); + await runner.verifyElementText( + userDashboardPage.dashboardFoundingAvailableCardText, + dashboardPageData.dashboardFoundingAvailableCardText, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardFoundingAvailableIcon, + ); + + await runner.waitUntilElementIsInvisible( + userDashboardPage.dashboardCardLoader, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardFoundingAvailablePotentialValue, + ); + + await runner.validateNumericValueFromElement( + userDashboardPage.dashboardFoundingAvailablePotentialValue, + ); + + await runner.waitUntilElementIsInvisible( + userDashboardPage.dashboardCardLoader, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardFoundingAvailableTotalValue, + ); + await runner.validateNumericValueFromElement( + userDashboardPage.dashboardFoundingAvailableTotalValue, + ); + }); + + test("Validating Dashboard All Researchers Card", async ({ + runner, + userDashboardPage, + }) => { + await runner.verifyElementIsVisible( + userDashboardPage.dashboardAllResearchersCardText, + ); + await runner.verifyElementText( + userDashboardPage.dashboardAllResearchersCardText, + dashboardPageData.dashboardAllResearchersCardText, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardAllResearchersIcon, + ); + + await runner.waitUntilElementIsInvisible( + userDashboardPage.dashboardCardLoader, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardAllResearchersValue, + ); + await runner.validateNumericValueFromElement( + userDashboardPage.dashboardAllResearchersValue, + ); + }); + + test("Validating Dashboard Founding Scenario Card", async ({ + runner, + userDashboardPage, + }) => { + await runner.verifyElementIsVisible( + userDashboardPage.dashboardFoundingScenarioButton, + ); + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardFoundingScenarioButton, + dashboardPageData.dashboardFoundingScenarioButton, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_MATCHES_PAGE_URL); + await runner.navigateBack(); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardFoundingScenarioIcon, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardFoundingScenarioText, + ); + await runner.verifyElementText( + userDashboardPage.dashboardFoundingScenarioText, + dashboardPageData.dashboardFoundingScenarioText, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardFoundingScenarioRechart, + ); + }); + + test("Validating No Notification in Recent Notification Section", async ({ + runner, + userDashboardPage, + }) => { + // View All Notifications Button + await runner.verifyElementIsVisible( + userDashboardPage.dashboardRecentMatchNotificationsButton, + ); + await runner.verifyElementIsVisible( + userDashboardPage.dashboardRecentMatchNotificationsButton, + ); + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardRecentMatchNotificationsButton, + dashboardPageData.dashboardRecentMatchNotificationsButton, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_NOTIFICATIONS_PAGE_URL); + await runner.navigateBack(); + + // Validate No Notification Or Have Notifications + await runner.waitUntilElementIsInvisible( + userDashboardPage.dashboardCardLoader, + ); + await runner.validateNoDataFound( + userDashboardPage.dashboardNoNotificationText, + userDashboardPage.dashboardNotificationLists, + dashboardPageData.noNotificationText, + ); + }); + + // No notification here, so some issue here + test("Validating Dashboard Recent Match Notifications Card", async ({ + runner, + userDashboardPage, + }) => { + await runner.verifyElementIsVisible( + userDashboardPage.dashboardRecentMatchNotificationsButton, + ); + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardRecentMatchNotificationsButton, + dashboardPageData.dashboardRecentMatchNotificationsButton, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_NOTIFICATIONS_PAGE_URL); + await runner.navigateBack(); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardRecentMatchNotificationsIcon, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardRecentMatchNotificationsText, + ); + await runner.verifyElementText( + userDashboardPage.dashboardRecentMatchNotificationsText, + dashboardPageData.dashboardRecentMatchNotificationsText, + ); + + // Single Notification Validation + await runner.validateTitleByRegex( + userDashboardPage.dashboardNotificationTitle, + ); + await runner.validateNotificationTexts( + userDashboardPage.dashboardNotificationText, + dashboardPageData.dashboardNotificationMatchTexts, + dashboardPageData.dashboardNotificationTexts, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardNotificationViewButton, + ); + await runner.verifyElementToHaveCSSProperty( + userDashboardPage.dashboardNotificationViewButton, + fundFitData.backgroundColorCss, + fundFitData.blueColorCode, + ); + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardNotificationViewButton, + dashboardPageData.dashboardNotificationViewButton, + ); + + await runner.waitUntilElementIsInvisible( + userDashboardPage.dashboardCardLoader, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.notificationModalHeader, + ); + await runner.verifyElementText( + userDashboardPage.notificationModalHeader, + [ + dashboardPageData.notificationModalHeader, + dashboardPageData.notificationModalHeader2, + ], + ); + + await runner.verifyElementIsVisible( + userDashboardPage.notificationModalNameLabel, + ); + await runner.verifyElementText( + userDashboardPage.notificationModalNameLabel, + dashboardPageData.notificationModalNameLabel, + ); + await runner.verifyElementIsVisible( + userDashboardPage.notificationModalName, + ); + await runner.verifyInputIsDisable( + userDashboardPage.notificationModalName, + ); + + await runner.verifyElementIsVisible( + userDashboardPage.notificationModalNotificationTypeSelectLabel, + ); + await runner.verifyElementText( + userDashboardPage.notificationModalNotificationTypeSelectLabel, + dashboardPageData.notificationModalNotificationTypeSelectLabel, + ); + }); + }); // End of describe block + } // End of runTests method +} // End of HomePageTest class +const testSuite = new UserDashboardPageTest(); +testSuite.runTests(); diff --git a/tests/UserLoginTest.spec.ts b/tests/UserLoginTest.spec.ts new file mode 100644 index 0000000..0687daf --- /dev/null +++ b/tests/UserLoginTest.spec.ts @@ -0,0 +1,578 @@ +import { test } from "../utilities/fixtures"; +import { ExpectedTextProvider } from "../utilities/valueProvider"; +import { ENV } from "../utilities/env"; +import { FakerDataProvider } from "../utilities/fakerDataProvider"; +import fundFitData from "../testData/fundFit.json"; +import registerPageData from "../testData/registerPageData.json"; +import loginPageData from "../testData/loginPageData.json"; +import homePageData from "../testData/homePageData.json"; +import dashboardPageData from "../testData/dashboardPageData.json"; + +class UserLoginTest extends ExpectedTextProvider { + private fakerData: FakerDataProvider; + constructor() { + super(); + this.fakerData = new FakerDataProvider(); + } + + runTests() { + const { email, password } = this.fakerData; + + test.describe("Validating User Login Scenarios", () => { + test.beforeEach(async ({ runner, userLandingPage }) => { + await runner.navigateTo(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyContainsUrl(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyPageTitle(fundFitData.fundFitTitle); + + await runner.verifyElementToHaveCSSProperty( + userLandingPage.loginButton, + fundFitData.textDecorationColorCss, + fundFitData.blackColorCode, + ); + await runner.validateAttribute( + userLandingPage.loginButton, + "href", + homePageData.loginButtonHrefValue, + ); + await runner.validateTextAndClickOnElement( + userLandingPage.loginButton, + homePageData.loginButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_LOGIN_PAGE_URL); + }); + + test("User Inputs Valid User Details To Login", async ({ + runner, + userLoginPage, + userDashboardPage, + }) => { + await runner.verifyElementIsVisible(userLoginPage.email); + await runner.enterText(userLoginPage.email, ENV.FUND_FIT_USER_EMAIL); + + await runner.verifyElementIsVisible(userLoginPage.password); + await runner.enterText( + userLoginPage.password, + ENV.FUND_FIT_USER_PASSWORD, + ); + await runner.verifyElementIsVisible(userLoginPage.eyeIcon); + await runner.clickOnElement(userLoginPage.eyeIcon); + await runner.validateAttribute(userLoginPage.password, "type", "text"); + await runner.verifyInputValue( + userLoginPage.password, + ENV.FUND_FIT_USER_PASSWORD, + ); + + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.validateTextAndClickOnElement( + userLoginPage.loginButton, + loginPageData.loginButtonText, + ); + + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + loginPageData.SuccessfulLoginToastText, + ); + + await runner.verifyContainsUrl(ENV.FUND_FIT_DASHBOARD_PAGE_URL); + await runner.verifyElementIsVisible(userDashboardPage.dashboardButton); + await runner.verifyElementText( + userDashboardPage.dashboardButton, + dashboardPageData.dashboardNavButtonText, + ); + }); + + test("User tries to login with random email", async ({ + runner, + userLoginPage, + }) => { + await runner.verifyElementIsVisible(userLoginPage.email); + await runner.enterText(userLoginPage.email, email); + await runner.verifyElementIsVisible(userLoginPage.password); + await runner.enterText(userLoginPage.password, password); + await runner.verifyElementIsVisible(userLoginPage.eyeIcon); + await runner.clickOnElement(userLoginPage.eyeIcon); + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.validateTextAndClickOnElement( + userLoginPage.loginButton, + loginPageData.loginButtonText, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + loginPageData.invalidEmailOrPasswordErrorMessage, + ); + }); + + test("User Tries To Login With Both Invalid Data ", async ({ + runner, + userLoginPage, + }) => { + // + + await runner.verifyElementIsVisible(userLoginPage.email); + await runner.verifyElementPlaceholder( + userLoginPage.email, + loginPageData.loginPageEmailPlaceholderText, + ); + await runner.enterText(userLoginPage.email, email); + await runner.verifyElementIsVisible(userLoginPage.password); + await runner.verifyElementPlaceholder( + userLoginPage.password, + loginPageData.loginPagePasswordPlaceholderText, + ); + await runner.enterText(userLoginPage.password, password); + await runner.verifyElementIsVisible(userLoginPage.eyeIcon); + await runner.clickOnElement(userLoginPage.eyeIcon); + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.validateTextAndClickOnElement( + userLoginPage.loginButton, + loginPageData.loginButtonText, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + loginPageData.invalidEmailOrPasswordErrorMessage, + ); + }); + + test("User Tries Login With Empty Email & Password", async ({ + runner, + userLoginPage, + }) => { + // + + await runner.verifyElementIsVisible(userLoginPage.emailInputBoxHeader); + await runner.verifyElementText( + userLoginPage.emailInputBoxHeader, + loginPageData.loginPageEmailInputBoxHeader, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.emailInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + await runner.verifyElementIsVisible( + userLoginPage.passwordInputBoxHeader, + ); + await runner.verifyElementText( + userLoginPage.passwordInputBoxHeader, + loginPageData.loginPagePasswordInputBoxHeader, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.passwordInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementToHaveCSSProperty( + userLoginPage.passwordInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + await runner.verifyElementIsVisible(userLoginPage.email); + await runner.clickOnElement(userLoginPage.email); + await runner.verifyElementIsVisible(userLoginPage.password); + await runner.clickOnElement(userLoginPage.password); + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.validateTextAndClickOnElement( + userLoginPage.loginButton, + loginPageData.loginButtonText, + ); + + await runner.verifyElementToHaveCSSProperty( + userLoginPage.emailInputBoxHeader, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.verifyElementIsVisible( + userLoginPage.emailInputBoxErrorMessage, + ); + await runner.verifyElementText( + userLoginPage.emailInputBoxErrorMessage, + loginPageData.loginPageEmailErrorMessage, + ); + + await runner.verifyElementToHaveCSSProperty( + userLoginPage.passwordInputBoxHeader, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.verifyElementIsVisible( + userLoginPage.passwordInputBoxErrorMessage, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.passwordInputBoxErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.verifyElementText( + userLoginPage.passwordInputBoxErrorMessage, + loginPageData.loginPagePasswordErrorMessage, + ); + }); + + //User tries login with empty email and valid password + test("User Tries To Login With Empty Email & Valid Password", async ({ + runner, + userLoginPage, + }) => { + // + + await runner.verifyElementIsVisible(userLoginPage.emailInputBoxHeader); + await runner.verifyElementText( + userLoginPage.emailInputBoxHeader, + loginPageData.loginPageEmailInputBoxHeader, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.emailInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible( + userLoginPage.passwordInputBoxHeader, + ); + await runner.verifyElementText( + userLoginPage.passwordInputBoxHeader, + loginPageData.loginPagePasswordInputBoxHeader, + ); + + await runner.verifyElementToHaveCSSProperty( + userLoginPage.passwordInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userLoginPage.password); + await runner.enterText( + userLoginPage.password, + ENV.FUND_FIT_USER_PASSWORD, + ); + + await runner.verifyElementIsVisible(userLoginPage.eyeIcon); + await runner.clickOnElement(userLoginPage.eyeIcon); + + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.loginButton, + fundFitData.backgroundColorCss, + fundFitData.orangeColorCode, + ); + await runner.validateTextAndClickOnElement( + userLoginPage.loginButton, + loginPageData.loginButtonText, + ); + + await runner.verifyElementIsVisible(userLoginPage.emailInputBoxHeader); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.emailInputBoxHeader, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.verifyElementIsVisible( + userLoginPage.emailInputBoxErrorMessage, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.emailInputBoxErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + }); + + //User tries login with empty email and invalid password + test("User Tries To Login With Empty Email & Invalid Password", async ({ + runner, + userLoginPage, + }) => { + // + + await runner.verifyElementIsVisible(userLoginPage.emailInputBoxHeader); + await runner.verifyElementText( + userLoginPage.emailInputBoxHeader, + loginPageData.loginPageEmailInputBoxHeader, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.emailInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible( + userLoginPage.passwordInputBoxHeader, + ); + await runner.verifyElementText( + userLoginPage.passwordInputBoxHeader, + loginPageData.loginPagePasswordInputBoxHeader, + ); + + await runner.verifyElementToHaveCSSProperty( + userLoginPage.passwordInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userLoginPage.email); + + await runner.verifyElementIsVisible(userLoginPage.password); + await runner.enterText(userLoginPage.password, password); + + await runner.verifyElementIsVisible(userLoginPage.eyeIcon); + await runner.clickOnElement(userLoginPage.eyeIcon); + + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.validateTextAndClickOnElement( + userLoginPage.loginButton, + loginPageData.loginButtonText, + ); + + await runner.verifyElementIsVisible(userLoginPage.emailInputBoxHeader); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.emailInputBoxHeader, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.verifyElementIsVisible( + userLoginPage.emailInputBoxErrorMessage, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.emailInputBoxErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + }); + + test("User Tries Login With Valid Email & Empty Password", async ({ + runner, + userLoginPage, + }) => { + // + + await runner.verifyElementIsVisible(userLoginPage.emailInputBoxHeader); + await runner.verifyElementText( + userLoginPage.emailInputBoxHeader, + loginPageData.loginPageEmailInputBoxHeader, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.emailInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible( + userLoginPage.passwordInputBoxHeader, + ); + await runner.verifyElementText( + userLoginPage.passwordInputBoxHeader, + loginPageData.loginPagePasswordInputBoxHeader, + ); + + await runner.verifyElementToHaveCSSProperty( + userLoginPage.passwordInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userLoginPage.email); + await runner.verifyElementPlaceholder( + userLoginPage.email, + loginPageData.loginPageEmailPlaceholderText, + ); + await runner.enterText(userLoginPage.email, ENV.FUND_FIT_USER_EMAIL); + + await runner.verifyElementIsVisible(userLoginPage.password); + await runner.verifyElementPlaceholder( + userLoginPage.password, + loginPageData.loginPagePasswordPlaceholderText, + ); + + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.clickOnElement(userLoginPage.loginButton); + + await runner.verifyElementIsVisible( + userLoginPage.passwordInputBoxErrorMessage, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.passwordInputBoxErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementText( + userLoginPage.passwordInputBoxErrorMessage, + loginPageData.loginPagePasswordErrorMessage, + ); + }); + + //User tries login with invalid email and empty password + test("User Tries Login With Invalid Email & Empty Password", async ({ + runner, + userLoginPage, + }) => { + // + + await runner.verifyElementIsVisible(userLoginPage.emailInputBoxHeader); + await runner.verifyElementText( + userLoginPage.emailInputBoxHeader, + loginPageData.loginPageEmailInputBoxHeader, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.emailInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible( + userLoginPage.passwordInputBoxHeader, + ); + await runner.verifyElementText( + userLoginPage.passwordInputBoxHeader, + loginPageData.loginPagePasswordInputBoxHeader, + ); + + await runner.verifyElementToHaveCSSProperty( + userLoginPage.passwordInputBoxHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userLoginPage.email); + await runner.verifyElementPlaceholder( + userLoginPage.email, + loginPageData.loginPageEmailPlaceholderText, + ); + await runner.enterText(userLoginPage.email, email); + + await runner.verifyElementIsVisible(userLoginPage.password); + await runner.verifyElementPlaceholder( + userLoginPage.password, + loginPageData.loginPagePasswordPlaceholderText, + ); + + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.clickOnElement(userLoginPage.loginButton); + + await runner.verifyElementIsVisible( + userLoginPage.passwordInputBoxErrorMessage, + ); + await runner.verifyElementToHaveCSSProperty( + userLoginPage.passwordInputBoxErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementText( + userLoginPage.passwordInputBoxErrorMessage, + loginPageData.loginPagePasswordErrorMessage, + ); + }); + + test("User Successfully Navigate To The Register Page ", async ({ + runner, + userLoginPage, + userRegisterPage, + }) => { + await runner.verifyElementIsVisible( + userLoginPage.loginPageRegisterButton, + ); + + await runner.validateTextAndClickOnElement( + userLoginPage.loginPageRegisterButton, + "Register", + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_REGISTER_PAGE_URL); + await runner.verifyElementIsVisible(userRegisterPage.heroText); + await runner.verifyElementText( + userRegisterPage.heroText, + registerPageData.registerPageHeroText, + ); + + await runner.verifyElementText( + userRegisterPage.heroText2, + registerPageData.registerPageHeroText2, + ); + }); + + // Flaky Test + test("User Navigates to the Forget Password page", async ({ + runner, + userLoginPage, + }) => { + await runner.verifyElementToHaveCSSProperty( + userLoginPage.loginButton, + fundFitData.textDecorationColorCss, + fundFitData.secondOrangeColor, + ); + + await runner.verifyElementIsVisible(userLoginPage.loginPageHeaderText); + await runner.verifyElementText( + userLoginPage.loginPageHeaderText, + "To access your account, log in below.", + ); + + await runner.verifyElementIsVisible(userLoginPage.forgetPasswordButton); + await runner.validateAttribute( + userLoginPage.forgetPasswordButton, + fundFitData.hrefCss, + loginPageData.forgetPasswordHrefValue, + ); + + await runner.validateTextAndClickOnElement( + userLoginPage.forgetPasswordButton, + loginPageData.forgetPasswordButtonText, + ); + + await runner.verifyContainsUrl(ENV.FUND_FIT_FORGET_PASSWORD_PAGE_URL); + + await runner.verifyElementIsVisible( + userLoginPage.forgetPasswordFormHeaderText, + ); + + await runner.verifyElementText( + userLoginPage.forgetPasswordFormSubHeaderText, + "No FundFit account? Request a Demo", + ); + + await runner.validateAttribute( + userLoginPage.forgetPasswordFormSubHeaderText, + fundFitData.hrefCss, + ENV.FUND_FIT_LANDING_PAGE_URL, + "a", + ); + + await runner.verifyElementText( + userLoginPage.ForgetPasswordEmailHeader, + loginPageData.loginPageEmailInputBoxHeader, + ); + + await runner.verifyElementPlaceholder( + userLoginPage.ForgetPasswordEmailInputBox, + loginPageData.loginPageEmailPlaceholderText, + ); + + await runner.enterText( + userLoginPage.ForgetPasswordEmailInputBox, + email, + ); + + await runner.validateTextAndClickOnElement( + userLoginPage.loginButton, + loginPageData.submitButtonText, + ); + + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + + await runner.verifyElementText( + userLoginPage.toastMessage, + loginPageData.forgetPasswordSuccessfulRequestText, + ); + }); + }); //Describe block + } //End of runTests method +} //End of UserLoginTest class +const testSuite = new UserLoginTest(); +testSuite.runTests(); diff --git a/tests/UserProfileTest.spec.ts b/tests/UserProfileTest.spec.ts new file mode 100644 index 0000000..494aeab --- /dev/null +++ b/tests/UserProfileTest.spec.ts @@ -0,0 +1,2612 @@ +import { test } from "../utilities/fixtures"; +import { ExpectedTextProvider } from "../utilities/valueProvider"; +import { ENV } from "../utilities/env"; +import fundFitData from "../testData/fundFit.json"; +import loginPageData from "../testData/loginPageData.json"; +import dashboardPageData from "../testData/dashboardPageData.json"; +import profilePageData from "../testData/profilePageData.json"; +import { FakerDataProvider } from "../utilities/fakerDataProvider"; + +class UserProfileTest extends ExpectedTextProvider { + private fakerData: FakerDataProvider; + constructor() { + super(); + this.fakerData = new FakerDataProvider(); + } + + runTests() { + const { + firstName, + lastName, + phoneNumber, + password, + bio, + randomCharValue30Digits, + randomCharValuesForInputValueValidation, + numericValueOf20Digits, + + backgroundType, + role, + department, + organization, + organizationAddress, + startDate, + endDate, + + workType, + journalTitle, + title, + publicationYear, + publicationMonth, + publicationDay, + + projectTypes, + projectTitle, + organizationType, + amount, + } = this.fakerData; + + test.describe("Validating Profile Page Scenarios", () => { + test.beforeEach( + async ({ + runner, + userLandingPage, + userLoginPage, + userDashboardPage, + userProfilePage, + }) => { + await runner.navigateTo(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyContainsUrl(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyPageTitle(fundFitData.fundFitTitle); + // await runner.waitForMilliseconds(10000); + // await runner.validateAttribute( + // userLandingPage.headerImage, + // "src", + // fundFitData.logoImageSource, + // ); + + await runner.verifyElementIsVisible(userLandingPage.loginButton); + await runner.validateTextAndClickOnElement( + userLandingPage.loginButton, + loginPageData.loginButtonText, + ); + + await runner.verifyElementIsVisible(userLoginPage.email); + await runner.enterText(userLoginPage.email, ENV.FUND_FIT_USER_EMAIL); + + await runner.verifyElementIsVisible(userLoginPage.password); + await runner.enterText( + userLoginPage.password, + ENV.FUND_FIT_USER_PASSWORD, + ); + await runner.verifyElementIsVisible(userLoginPage.eyeIcon); + await runner.clickOnElement(userLoginPage.eyeIcon); + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.validateTextAndClickOnElement( + userLoginPage.loginButton, + loginPageData.loginButtonText, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + loginPageData.SuccessfulLoginToastText, + ); + + await runner.waitForMilliseconds(5); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardNavbarAccountButton, + ); + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardNavbarAccountButton, + "Account", + ); + + await runner.verifyElementIsVisible( + userDashboardPage.dashboardNavbarAccountProfileText, + ); + await runner.validateAttribute( + userDashboardPage.dashboardNavbarAccountProfileText, + "href", + dashboardPageData.dashboardAccountProfileHrefValue, + ); + await runner.validateTextAndClickOnElement( + userDashboardPage.dashboardNavbarAccountProfileText, + dashboardPageData.dashboardNavAccountProfileText, + ); + + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_PAGE_URL); + await runner.verifyElementTextContains( + userProfilePage.profileHeaderText, + profilePageData.profileHeaderText, + ); + }, + ); + + // 🔹 Profile Page - Test Case Scenarios + test("Validating Profile Page Side Bar Profile Information", async ({ + runner, + userProfilePage, + }) => { + await runner.verifyElementIsVisible(userProfilePage.sidebarPhoto); + + await runner.verifyElementIsVisible( + userProfilePage.sidebarFullNameText, + ); + await runner.validateNameByRegex( + userProfilePage.sidebarFullNameText, + "FULL_NAME", + ); + + await runner.verifyElementIsVisible(userProfilePage.sidebarTitleText); + await runner.verifyElementText( + userProfilePage.sidebarTitleText, + profilePageData.profileTitle, + ); + }); + + test("Validating Profile Page Side Bar Navigation", async ({ + runner, + userProfilePage, + }) => { + // 🔸 Sidebar Navigation Scenarios + // 1. Verify that clicking on each tab (General, Background, Works, Projects, Attachments) switches the active view. + await runner.clickOnElement(userProfilePage.sidebarBackgroundButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_PAGE_URL, + ); + await runner.verifyElementText( + "xpath=//h1[normalize-space()='Background']", + "Background", + ); + + await runner.clickOnElement(userProfilePage.sidebarWorksButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_WORK_PAGE_URL); + await runner.verifyElementText( + "xpath=//h1[normalize-space()='Research Works']", + "Research Works", + ); + + await runner.clickOnElement(userProfilePage.sidebarProjectsButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_PROJECT_PAGE_URL); + await runner.verifyElementText( + "xpath=//h1[normalize-space()='Research Projects']", + "Research Projects", + ); + + await runner.clickOnElement(userProfilePage.sidebarAttachmentsButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_ATTACHMENT_PAGE_URL, + ); + await runner.verifyElementText( + "xpath=//h1[normalize-space()='Attachments']", + "Attachments", + ); + + await runner.clickOnElement(userProfilePage.sidebarGeneralInfoButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_PAGE_URL); + + // 2. Verify that each section shows corresponding data or a "0" count if no entries are present. + + // Side Bar Button Visibility Check + await runner.verifyElementIsVisible([ + userProfilePage.sidebarGeneralInfoText, + userProfilePage.sidebarBackgroundText, + userProfilePage.sidebarWorksText, + userProfilePage.sidebarProjectsText, + userProfilePage.sidebarAttachmentsText, + ]); + await runner.verifyElementIsVisible([ + userProfilePage.sidebarGeneralInfoIcon, + userProfilePage.sidebarBackgroundIcon, + userProfilePage.sidebarWorksIcon, + userProfilePage.sidebarProjectsIcon, + userProfilePage.sidebarAttachmentsIcon, + ]); + await runner.verifyElementIsVisible([ + userProfilePage.sidebarBackgroundValue, + userProfilePage.sidebarWorksValue, + userProfilePage.sidebarProjectsValue, + userProfilePage.sidebarAttachmentsValue, + ]); + + // Side Bar Button Text Validity Check + await runner.verifyElementText( + userProfilePage.sidebarGeneralInfoText, + profilePageData.sidebarGeneralInfoButtonText, + ); + await runner.verifyElementText( + userProfilePage.sidebarBackgroundText, + profilePageData.sidebarBackgroundButtonText, + ); + await runner.verifyElementText( + userProfilePage.sidebarWorksText, + profilePageData.sidebarWorksButtonText, + ); + await runner.verifyElementText( + userProfilePage.sidebarProjectsText, + profilePageData.sidebarProjectsButtonText, + ); + await runner.verifyElementText( + userProfilePage.sidebarAttachmentsText, + profilePageData.sidebarAttachmentsButtonText, + ); + + // 3. Verify that inactive tabs are not highlighted and have no visible content shown by default. + // Side Bar + await runner.verifyElementToHaveCSSProperty( + userProfilePage.sidebarGeneralInfoText, + fundFitData.textColorCss, + fundFitData.orangeColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + [ + userProfilePage.sidebarBackgroundText, + userProfilePage.sidebarWorksText, + userProfilePage.sidebarProjectsText, + userProfilePage.sidebarAttachmentsText, + ], + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + // Top Bar + await runner.validateAttribute( + userProfilePage.topBarGeneralButton, + "data-state", + "active", + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.topBarGeneralButton, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.validateAttribute( + [ + userProfilePage.topBarBackgroundButton, + userProfilePage.topBarWorksButton, + userProfilePage.topBarProjectsButton, + userProfilePage.topBarAttachmentsButton, + ], + "data-state", + "inactive", + ); + await runner.verifyElementToHaveCSSProperty( + [ + userProfilePage.topBarBackgroundButton, + userProfilePage.topBarWorksButton, + userProfilePage.topBarProjectsButton, + userProfilePage.topBarAttachmentsButton, + ], + fundFitData.textColorCss, + fundFitData.grayColorCode, + ); + }); + + // ORCID Email & Password Not Found + test.skip("Validating Profile Page Side Bar ORCID Connection", async ({ + runner, + userProfilePage, + }) => { + // 🔸 ORCID Integration Scenario + // 1. Verify that clicking the “Disconnect from ORCID” button prompts the user for confirmation. + // 2. Verify that upon confirming disconnection, the ORCID link is removed and a success message is displayed. + + await runner.verifyElementIsVisible(userProfilePage.sidebarORCIDButton); + }); + + // ORCID Email & Password Not Found + test.skip("Validating Profile Page Side Bar ORCID Disconnection", async ({ + runner, + userProfilePage, + }) => { + await runner.verifyElementIsVisible(userProfilePage.sidebarORCIDButton); + }); + + test("Validating Profile Page Generale Information Section", async ({ + runner, + userProfilePage, + }) => { + // 🔸 General Tab Scenarios + // 1. Verify that all user profile fields (First Name, Last Name, Email, Institution) are displayed correctly. + await runner.verifyElementIsVisible([ + userProfilePage.firstNameHeader, + userProfilePage.lastNameHeader, + userProfilePage.emailHeader, + userProfilePage.institutionHeader, + userProfilePage.phoneNumberHeader, + userProfilePage.biographyHeader, + ]); + + await runner.verifyElementTextContains( + [ + userProfilePage.firstNameHeader, + userProfilePage.lastNameHeader, + userProfilePage.emailHeader, + userProfilePage.institutionHeader, + userProfilePage.phoneNumberHeader, + userProfilePage.biographyHeader, + ], + [ + profilePageData.firstNameHeaderText, + profilePageData.lastNameHeaderText, + profilePageData.emailHeaderText, + profilePageData.institutionHeaderText, + profilePageData.phoneNumberHeaderText, + profilePageData.biographyHeaderText, + ], + ); + + await runner.validateNameByRegex( + userProfilePage.firstName, + "FIRST_NAME", + ); + await runner.validateNameByRegex(userProfilePage.lastName, "LAST_NAME"); + await runner.verifyElementText( + userProfilePage.email, + ENV.FUND_FIT_USER_EMAIL, + ); + + // 2. Verify that optional fields (Phone Number, Biography) show a placeholder or “Not provided” if left empty. + await runner.validatePhoneNumberByRegex(userProfilePage.phoneNumber); + await runner.validateUserBiographyByRegex(userProfilePage.biography); + + // 3. Verify that clicking the "Edit Profile" button enables profile field editing or opens an editable form/modal. + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementTextContains( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.backAndCancelButton, + profilePageData.cancelButtonText, + ); + + // 4. Verify that the "Password Settings" button navigates to or opens the password update section. + await runner.validateTextAndClickOnElement( + userProfilePage.passwordSettings, + profilePageData.passwordSettingsButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_PASSWORD_SETTING_PAGE_URL, + ); + await runner.verifyElementTextContains( + userProfilePage.passwordSettingsHeaderText, + profilePageData.passwordSettingsHeaderText, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.backAndCancelButton, + profilePageData.cancelButtonText, + ); + + // Website er profile "Email Settings" Button ta akhon hide kore dise. + // 5 Verify that the "Email Settings" button navigates to or opens the email update section. + // await runner.validateTextAndClickOnElement( + // userProfilePage.emailSettings, + // profilePageData.emailSettingsButtonText, + // ); + // await runner.verifyContainsUrl( + // ENV.FUND_FIT_PROFILE_EMAIL_SETTING_PAGE_URL, + // ); + // await runner.verifyElementTextContains( + // userProfilePage.emailSettingsHeaderText, + // profilePageData.emailSettingsHeaderText, + // ); + // await runner.validateTextAndClickOnElement( + // userProfilePage.cancelButton, + // profilePageData.cancelButtonText, + // ); + }); + + // 🔹 Update Profile Page – Test Case Scenarios + test("Validating Update User Profile Information", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click on user edit profile + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementText( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + + await runner.enterText(userProfilePage.editProfileFirstName, firstName); + await runner.enterText(userProfilePage.editProfileLastName, lastName); + await runner.enterText( + userProfilePage.editProfilePhoneNumber, + phoneNumber, + ); + await runner.enterText(userProfilePage.editProfileBiography, bio); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyProfileUpdateToast, + ); + await runner.waitForMilliseconds(5); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + }); + + test("Validating User Update Profile Page Fields Are Pre-Populated With the User's Existing Information", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click On Edit Profile + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementText( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + + // 1. Verify that the All fields are pre-populated with the user's existing information. + await runner.enterText( + userProfilePage.editProfileFirstName, + profilePageData.userFirstName, + ); + await runner.enterText( + userProfilePage.editProfileLastName, + profilePageData.userLastName, + ); + await runner.enterText( + userProfilePage.editProfilePhoneNumber, + phoneNumber, + ); + await runner.enterText(userProfilePage.editProfileBiography, bio); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyProfileUpdateToast, + ); + await runner.waitForMilliseconds(1000); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.profileHeaderText); + await runner.verifyElementText( + userProfilePage.profileHeaderText, + profilePageData.profileHeaderText, + ); + + // Click On Edit Profile + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementText( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + + await runner.waitForMilliseconds(1000); + + await runner.verifyInputValue( + userProfilePage.editProfileFirstName, + profilePageData.userFirstName, + ); + await runner.verifyInputValue( + userProfilePage.editProfileLastName, + profilePageData.userLastName, + ); + await runner.verifyInputValue( + userProfilePage.editProfilePhoneNumber, + phoneNumber, + ); + await runner.verifyInputValue( + userProfilePage.editProfileBiography, + bio, + ); + }); + + test("Verify that the Biography field accepts text input and supports multiline entries", async ({ + runner, + userProfilePage, + }) => { + // Click On Edit Profile + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementText( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + + // 3. Verify that the "Biography" field accepts text input and supports multiline entries. + await runner.clearInputField(userProfilePage.editProfileBiography); + await runner.enterText(userProfilePage.editProfileBiography, bio); + await runner.clickOnElement(userProfilePage.submitButton); + + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.biography); + await runner.waitForMilliseconds(1000); + await runner.verifyElementText(userProfilePage.biography, bio); + }); + + // Flaky Test + test("User Interaction with First Name", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click on user edit profile + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementText( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + + // 4. Verify that required fields (e.g., First Name, Last Name) show validation messages if left empty. + // Empty Check + await runner.clearInputField(userProfilePage.editProfileFirstName); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementText( + userProfilePage.editProfileFirstNameErrorText, + profilePageData.editProfileFirstNameErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileFirstNameErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileFirstNameHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + // Alphanumeric Check + await runner.enterText( + userProfilePage.editProfileFirstName, + randomCharValue30Digits, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementText( + userProfilePage.editProfileFirstNameErrorText, + profilePageData.editProfileFirstNameErrorText2, // Error Appears First Name + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileFirstNameErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileFirstNameHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + // Max Length Check + await runner.enterText( + userProfilePage.editProfileFirstName, + randomCharValuesForInputValueValidation, + ); + + // await runner.validateTextAndClickOnElement( + // userProfilePage.submitButton, + // profilePageData.submitButtonText, + // ); + + await runner.verifyElementIsVisible( + userProfilePage.editProfileFirstNameErrorText, + ); + await runner.verifyElementText( + userProfilePage.editProfileFirstNameErrorText, + profilePageData.editProfileFirstNameLastNameErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileFirstNameErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileFirstNameHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + // Valid First Name Check + await runner.enterText(userProfilePage.editProfileFirstName, firstName); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + // await runner.verifyElementText( + // userLoginPage.toastMessage, + // profilePageData., + // ); + await runner.waitForMilliseconds(500); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + }); + + // Flaky Test + test("User Interaction With Last Name", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click on user edit profile + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementText( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + // Empty Check + await runner.clickOnElement(userProfilePage.editProfileLastName); + await runner.clearInputField(userProfilePage.editProfileLastName); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementText( + userProfilePage.editProfileLastNameErrorText, + profilePageData.editProfileLastNameErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileLastNameErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileLastNameHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + // Alphanumeric Check + await runner.enterText( + userProfilePage.editProfileLastName, + randomCharValue30Digits, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementText( + userProfilePage.editProfileLastNameErrorText, + profilePageData.editProfileLastNameErrorText2, // Error Appears Last Name + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileLastNameErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileLastNameHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + // Max Length Check + await runner.enterText( + userProfilePage.editProfileLastName, + randomCharValuesForInputValueValidation, + ); + // await runner.validateTextAndClickOnElement( + // userProfilePage.submitButton, + // profilePageData.submitButtonText, + // ); + + await runner.verifyElementIsVisible( + userProfilePage.editProfileLastNameErrorText, + ); + await runner.verifyElementText( + userProfilePage.editProfileLastNameErrorText, + profilePageData.editProfileFirstNameLastNameErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileLastNameErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfileLastNameHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + // Valid Last Name Check + await runner.enterText(userProfilePage.editProfileLastName, lastName); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + // await runner.verifyElementText( + // userLoginPage.toastMessage, + // profilePageData.successfullyProfileUpdateToast, + // ); + await runner.waitForMilliseconds(1000); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + }); + + // Flaky Test + test("User Interaction With Phone Number", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click on user edit profile + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementIsVisible( + userProfilePage.editProfileHeaderText, + ); + await runner.verifyElementText( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + + // Alphanumeric Check + await runner.enterText( + userProfilePage.editProfilePhoneNumber, + randomCharValuesForInputValueValidation, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementText( + userProfilePage.editProfilePhoneNumberErrorText, + profilePageData.editProfilePhoneNumberErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfilePhoneNumberErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfilePhoneNumberHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + // Max Length Check + await runner.enterText( + userProfilePage.editProfilePhoneNumber, + numericValueOf20Digits, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + + await runner.verifyElementIsVisible( + userProfilePage.editProfilePhoneNumberErrorText2, + ); + await runner.verifyElementText( + userProfilePage.editProfilePhoneNumberErrorText2, + profilePageData.editProfilePhoneNumberErrorText2, // Error Appears Phone Number + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfilePhoneNumberErrorText2, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.editProfilePhoneNumberHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + // Valid Phone Number Check + await runner.enterText( + userProfilePage.editProfilePhoneNumber, + phoneNumber, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.waitForMilliseconds(1000); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyProfileUpdateToast, + ); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + }); + + test("Validating User Update To Click On Cancel Button & Navigates Back To The Profile", async ({ + runner, + userProfilePage, + }) => { + // Click on user edit profile + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementText( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + + // 7. Verify that clicking the "Cancel" button discards any changes and navigates back to the profile view page. + await runner.enterText(userProfilePage.editProfileFirstName, firstName); + await runner.validateTextAndClickOnElement( + userProfilePage.backAndCancelButton, + profilePageData.cancelButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_PAGE_URL); + }); + + test("Validating User Update Profile Input Placeholders", async ({ + runner, + userProfilePage, + }) => { + // On click user edit profile + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementText( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + + // Edit Profile Input Fields + await runner.clearInputField(userProfilePage.editProfilePhoneNumber); + await runner.clearInputField(userProfilePage.editProfileBiography); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + + // After Click On Submit + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_PAGE_URL); + await runner.wait(1); + await runner.verifyElementText( + userProfilePage.profileHeaderText, + profilePageData.profileHeaderText, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.editProfile, + profilePageData.EditProfileButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_EDIT_PAGE_URL); + await runner.verifyElementText( + userProfilePage.editProfileHeaderText, + profilePageData.editProfileHeaderText, + ); + + await runner.waitForMilliseconds(1000); + + // Edit Profile Input Fields Placeholder + await runner.verifyElementPlaceholder( + userProfilePage.editProfilePhoneNumber, + profilePageData.editProfilePhoneNumberPlaceholderText, + ); + await runner.verifyElementPlaceholder( + userProfilePage.editProfileBiography, + profilePageData.editProfileBiographyPlaceholderText, + ); + }); + + // 🔹 Password Settings Page – Test Case Scenarios + test("Validating User Password Settings Header & Placeholder Texts", async ({ + runner, + userProfilePage, + }) => { + // Click on password settings + await runner.validateTextAndClickOnElement( + userProfilePage.passwordSettings, + profilePageData.passwordSettingsButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_PASSWORD_SETTING_PAGE_URL, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsHeaderText, + profilePageData.passwordSettingsHeaderText, + ); + // Header Text + await runner.verifyElementIsVisible( + userProfilePage.passwordSettingsNewPasswordHeaderText, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsNewPasswordHeaderText, + profilePageData.passwordSettingsNewPasswordHeaderText, + ); + // New Password + await runner.verifyElementIsVisible( + userProfilePage.passwordSettingsNewPasswordHeaderText, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsNewPasswordHeaderText, + profilePageData.passwordSettingsNewPasswordHeaderText, + ); + await runner.verifyElementPlaceholder( + userProfilePage.passwordSettingsNewPasswordInput, + profilePageData.passwordSettingsNewPasswordPlaceholderText, + ); + // Confirm Password + await runner.verifyElementIsVisible( + userProfilePage.passwordSettingsConfirmNewPasswordHeaderText, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsConfirmNewPasswordHeaderText, + profilePageData.passwordSettingsConfirmNewPasswordHeaderText, + ); + await runner.verifyElementPlaceholder( + userProfilePage.passwordSettingsConfirmNewPasswordInput, + profilePageData.passwordSettingsConfirmNewPasswordPlaceholderText, + ); + }); + + // Don't run this test. otherwise, it will change the password + test.skip("Validating User Password Successfully Changed", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click on password settings + await runner.validateTextAndClickOnElement( + userProfilePage.passwordSettings, + profilePageData.passwordSettingsButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_PASSWORD_SETTING_PAGE_URL, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsHeaderText, + profilePageData.passwordSettingsHeaderText, + ); + + await runner.enterText( + userProfilePage.passwordSettingsNewPasswordInput, + ENV.FUND_FIT_TEST_PASSWORD, + ); + await runner.enterText( + userProfilePage.passwordSettingsConfirmNewPasswordInput, + ENV.FUND_FIT_TEST_PASSWORD, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyProfileUpdateToast, + ); + await runner.waitForMilliseconds(5); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + }); + + test("User Tries To Change Password With Both Don't Match Password", async ({ + runner, + userProfilePage, + }) => { + // Click on password settings + await runner.validateTextAndClickOnElement( + userProfilePage.passwordSettings, + profilePageData.passwordSettingsButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_PASSWORD_SETTING_PAGE_URL, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsHeaderText, + profilePageData.passwordSettingsHeaderText, + ); + // New Password + await runner.enterText( + userProfilePage.passwordSettingsNewPasswordInput, + password, + ); + // Confirm New Password + await runner.enterText( + userProfilePage.passwordSettingsConfirmNewPasswordInput, + randomCharValue30Digits, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsConfirmNewPasswordErrorText, + profilePageData.passwordSettingsConfirmNewPasswordErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.passwordSettingsConfirmNewPasswordErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.passwordSettingsConfirmNewPasswordHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + }); + + test("User Tries To Change Password With Previous Password Value", async ({ + runner, + userProfilePage, + }) => { + // Click on password settings + await runner.validateTextAndClickOnElement( + userProfilePage.passwordSettings, + profilePageData.passwordSettingsButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_PASSWORD_SETTING_PAGE_URL, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsHeaderText, + profilePageData.passwordSettingsHeaderText, + ); + + // New Password + await runner.enterText( + userProfilePage.passwordSettingsNewPasswordInput, + ENV.FUND_FIT_USER_PREVIOUS_PASSWORD, + ); + // Confirm New Password + await runner.enterText( + userProfilePage.passwordSettingsConfirmNewPasswordInput, + ENV.FUND_FIT_USER_PREVIOUS_PASSWORD, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsConfirmNewPasswordErrorText2, + profilePageData.passwordSettingsConfirmNewPasswordErrorText2, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.passwordSettingsConfirmNewPasswordErrorText2, + fundFitData.textColorCss, + fundFitData.red500ColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.passwordSettingsConfirmNewPasswordHeaderText, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + }); + + test("User Tries To Change Password With Current Password Value", async ({ + runner, + userProfilePage, + }) => { + // Click on password settings + await runner.validateTextAndClickOnElement( + userProfilePage.passwordSettings, + profilePageData.passwordSettingsButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_PASSWORD_SETTING_PAGE_URL, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsHeaderText, + profilePageData.passwordSettingsHeaderText, + ); + + // New Password + await runner.enterText( + userProfilePage.passwordSettingsNewPasswordInput, + ENV.FUND_FIT_USER_PASSWORD, + ); + // Confirm New Password + await runner.enterText( + userProfilePage.passwordSettingsConfirmNewPasswordInput, + ENV.FUND_FIT_USER_PASSWORD, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementText( + userProfilePage.passwordSettingsConfirmNewPasswordErrorText2, + profilePageData.passwordSettingsConfirmNewPasswordErrorText2, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.passwordSettingsConfirmNewPasswordErrorText2, + fundFitData.textColorCss, + fundFitData.red500ColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.passwordSettingsConfirmNewPasswordHeaderText, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + }); + + // 🔹 Email Settings Page – Test Case Scenarios + // Don't run this test. otherwise, it will change the email + test.skip("Validating User Email Successfully Update", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click on email settings + await runner.validateTextAndClickOnElement( + userProfilePage.emailSettings, + profilePageData.emailSettingsButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_EMAIL_SETTING_PAGE_URL, + ); + await runner.verifyElementText( + userProfilePage.emailSettingsHeaderText, + profilePageData.emailSettingsHeaderText, + ); + + // Existing Email section + await runner.verifyElementText( + userProfilePage.emailSettingsCurrentEmailHeaderText, + profilePageData.emailSettingsCurrentEmailHeaderText, + ); + await runner.verifyElementText( + userProfilePage.emailSettingsCurrentEmailText, + ENV.FUND_FIT_USER_EMAIL, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.emailSettingsEditEmailButton, + profilePageData.emailSettingsEditEmailButtonText, + ); + + // New Email setup section + await runner.verifyElementText( + userProfilePage.emailSettingsNewEmailFrom, + profilePageData.emailSettingsNewEmailHeaderText, + "label", + ); + await runner.verifyInputValue( + userProfilePage.emailSettingsNewEmailFrom, + ENV.FUND_FIT_USER_EMAIL, + "input", + ); + await runner.enterText( + userProfilePage.emailSettingsNewEmailFrom, + ENV.FUND_FIT_USER_NEW_EMAIL, // need to change new email (FUND_FIT_USER_NEW_EMAIL) in .env file + "input", + ); + + // Submit button + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyProfileUpdateToast, + ); + await runner.waitForMilliseconds(2000); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementIsVisible(userProfilePage.toastCloseButton); + await runner.clickOnElement(userProfilePage.toastCloseButton); + }); + + test.skip("Validating User Email Change With Existing Email", async ({ + runner, + userProfilePage, + }) => { + // Click on email settings + await runner.verifyElementIsVisible(userProfilePage.emailSettings); + await runner.validateTextAndClickOnElement( + userProfilePage.emailSettings, + profilePageData.emailSettingsButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_EMAIL_SETTING_PAGE_URL, + ); + await runner.verifyElementText( + userProfilePage.emailSettingsHeaderText, + profilePageData.emailSettingsHeaderText, + ); + + // Existing Email section + await runner.verifyElementText( + userProfilePage.emailSettingsCurrentEmailHeaderText, + profilePageData.emailSettingsCurrentEmailHeaderText, + ); + await runner.verifyElementText( + userProfilePage.emailSettingsCurrentEmailText, + ENV.FUND_FIT_USER_EMAIL, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.emailSettingsEditEmailButton, + profilePageData.emailSettingsEditEmailButtonText, + ); + + // New Email section + await runner.verifyElementText( + userProfilePage.emailSettingsNewEmailFrom, + profilePageData.emailSettingsNewEmailHeaderText, + "label", + ); + await runner.enterText( + userProfilePage.emailSettingsNewEmailFrom, + ENV.FUND_FIT_USER_EMAIL, + "input", + ); + + // Submit button + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementText( + userProfilePage.emailSettingsAlreadyVerifiedErrorText, + profilePageData.emailSettingsAlreadyVerifiedErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.emailSettingsAlreadyVerifiedErrorText, + fundFitData.textColorCss, + fundFitData.red500ColorCode, + ); + }); + + test.skip("Validating User Email Invalid Email Format", async ({ + runner, + userProfilePage, + }) => { + // Click on email settings + await runner.validateTextAndClickOnElement( + userProfilePage.emailSettings, + profilePageData.emailSettingsButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_EMAIL_SETTING_PAGE_URL, + ); + await runner.verifyElementText( + userProfilePage.emailSettingsHeaderText, + profilePageData.emailSettingsHeaderText, + ); + + // New Email section + await runner.validateTextAndClickOnElement( + userProfilePage.emailSettingsEditEmailButton, + profilePageData.emailSettingsEditEmailButtonText, + ); + await runner.verifyElementText( + userProfilePage.emailSettingsEditEmailHeaderText, + profilePageData.emailSettingsEditEmailHeaderText, + ); + + // without @ value + await runner.enterText( + userProfilePage.emailSettingsNewEmailFrom, + `${randomCharValue30Digits}.com`, + "input", + ); + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyElementText( + userProfilePage.emailSettingsInvalidErrorText, + profilePageData.emailSettingsInvalidErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.emailSettingsInvalidErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + `${userProfilePage.emailSettingsNewEmailFrom} label`, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + // without .com value + await runner.enterText( + userProfilePage.emailSettingsNewEmailFrom, + `${firstName}@${lastName}`, + "input", + ); + await runner.clickOnElement(userProfilePage.submitButton); + await runner.verifyElementText( + userProfilePage.emailSettingsInvalidErrorText, + profilePageData.emailSettingsInvalidErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.emailSettingsInvalidErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + `${userProfilePage.emailSettingsNewEmailFrom} label`, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + // without domain value + await runner.enterText( + userProfilePage.emailSettingsNewEmailFrom, + `${firstName}@.com`, + "input", + ); + await runner.clickOnElement(userProfilePage.submitButton); + await runner.verifyElementText( + userProfilePage.emailSettingsInvalidErrorText, + profilePageData.emailSettingsInvalidErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.emailSettingsInvalidErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + `${userProfilePage.emailSettingsNewEmailFrom} label`, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + // without domain and .com value + await runner.enterText( + userProfilePage.emailSettingsNewEmailFrom, + `${firstName}`, + "input", + ); + await runner.clickOnElement(userProfilePage.submitButton); + await runner.verifyElementText( + userProfilePage.emailSettingsInvalidErrorText, + profilePageData.emailSettingsInvalidErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.emailSettingsInvalidErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + `${userProfilePage.emailSettingsNewEmailFrom} label`, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + }); + + test.skip("Validating Blank Email On User Email Field", async ({ + runner, + userProfilePage, + }) => { + // Click on email settings + await runner.validateTextAndClickOnElement( + userProfilePage.emailSettings, + profilePageData.emailSettingsButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_EMAIL_SETTING_PAGE_URL, + ); + await runner.verifyElementText( + userProfilePage.emailSettingsHeaderText, + profilePageData.emailSettingsHeaderText, + ); + + // New Email section + await runner.clickOnElement( + userProfilePage.emailSettingsEditEmailButton, + ); + await runner.verifyElementText( + userProfilePage.emailSettingsEditEmailHeaderText, + profilePageData.emailSettingsEditEmailHeaderText, + ); + + // Clear Email input field + await runner.clearInputField( + `${userProfilePage.emailSettingsNewEmailFrom} input`, + ); + await runner.clickOnElement(userProfilePage.submitButton); + await runner.verifyElementText( + userProfilePage.emailSettingsInvalidErrorText, + profilePageData.emailSettingsRequiredErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.emailSettingsInvalidErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + `${userProfilePage.emailSettingsNewEmailFrom} label`, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + }); + + // 🔹 Background Tab – Test Case Scenarios + test("Validating Navigation & Background Tab Activation Test", async ({ + runner, + userProfilePage, + }) => { + // 1. Verify that clicking the "Background" tab activates it and loads the correct content. + // Click on user background tab + await runner.clickOnElement(userProfilePage.topBarBackgroundButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.backgroundHeaderText, + ); + await runner.verifyElementText( + userProfilePage.backgroundHeaderText, + profilePageData.backgroundHeaderText, + ); + + // 2. Verify that other tabs (General, Works, Projects, Attachments) are not active when "Background" is selected. + await runner.validateAttribute( + [ + userProfilePage.topBarGeneralButton, + userProfilePage.topBarWorksButton, + userProfilePage.topBarProjectsButton, + userProfilePage.topBarAttachmentsButton, + ], + "data-state", + "inactive", + ); + }); + + test("Validating No Background Data Exists", async ({ + runner, + userProfilePage, + }) => { + // Click on user background tab + await runner.clickOnElement(userProfilePage.topBarBackgroundButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.backgroundHeaderText, + ); + await runner.verifyElementText( + userProfilePage.backgroundHeaderText, + profilePageData.backgroundHeaderText, + ); + + // Verify that the "No Background" message is displayed when there are no background entries. + await runner.verifyElementIsVisible([ + userProfilePage.noBackgroundIcon, + userProfilePage.noBackgroundHeaderText, + userProfilePage.noBackgroundText, + userProfilePage.noBackgroundAddButton, + ]); + await runner.verifyElementTextContains( + [ + userProfilePage.noBackgroundHeaderText, + userProfilePage.noBackgroundText, + userProfilePage.noBackgroundAddButton, + ], + [ + profilePageData.noBackgroundHeaderText, + profilePageData.noBackgroundText, + profilePageData.noBackgroundAddButtonText, + ], + ); + }); + + test("Validating Add Background Successfully & Remove Successfully", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click on user background tab + await runner.clickOnElement(userProfilePage.topBarBackgroundButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.backgroundHeaderText, + ); + await runner.verifyElementText( + userProfilePage.backgroundHeaderText, + profilePageData.backgroundHeaderText, + ); + + // Click on Add Background + await runner.verifyElementIsVisible( + userProfilePage.backgroundPlusIconButton, + ); + await runner.clickOnElement(userProfilePage.backgroundPlusIconButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_CREATE_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.createBackgroundHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createBackgroundHeaderText, + profilePageData.createBackgroundHeaderText, + ); + + // Select Background Type + await runner.selectDropdownOptionByText( + userProfilePage.createBackgroundTypeButtonText, + backgroundType, + ); + + // Add Role + await runner.enterText(userProfilePage.createBackgroundRoleInput, role); + // Add Department + await runner.enterText( + userProfilePage.createBackgroundDepartmentInput, + department, + ); + // Add Organization + await runner.enterText( + userProfilePage.createBackgroundOrganizationInput, + organization, + ); + // Add Organization Address + await runner.enterText( + userProfilePage.createBackgroundOrganizationAddressInput, + organizationAddress, + ); + // Add Start Year + await runner.enterText( + userProfilePage.createBackgroundStartYearInput, + startDate.year.toString(), + ); + // Add Start Month + await runner.enterText( + userProfilePage.createBackgroundStartMonthInput, + startDate.month.toString(), + ); + // Add Start Day + await runner.enterText( + userProfilePage.createBackgroundStartDayInput, + startDate.day.toString(), + ); + // Add End Year + await runner.enterText( + userProfilePage.createBackgroundEndYearInput, + endDate.year.toString(), + ); + // Add End Month + await runner.enterText( + userProfilePage.createBackgroundEndMonthInput, + endDate.month.toString(), + ); + // Add End Day + await runner.enterText( + userProfilePage.createBackgroundEndDayInput, + endDate.day.toString(), + ); + + // Click on Submit Button + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_PAGE_URL, + ); + await runner.waitForMilliseconds(1000); + await runner.verifyElementIsVisible(userProfilePage.table); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyBackgroundCreateToast, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.clickOnElement(userProfilePage.toastCloseButton); + + await runner.verifyElementIsVisible( + userProfilePage.sidebarBackgroundValue, + ); + await runner.verifyElementText( + userProfilePage.sidebarBackgroundValue, + "1", + ); + + // Remove Background + await runner.clickOnElement(userProfilePage.tableDeleteButton); + + // Open Background Delete Modal + await runner.verifyElementIsVisible( + userProfilePage.deleteModalHeaderText, + ); + await runner.verifyElementText( + userProfilePage.deleteModalHeaderText, + profilePageData.deleteModalHeaderText, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.deleteModalButton, + profilePageData.deleteModalDeleteButtonText, + ); + await runner.waitForMilliseconds(1000); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyBackgroundDeleteToast, + ); + }); + + test("Validating Background Data Update Successfully & Remove Successfully", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click on user background tab + await runner.clickOnElement(userProfilePage.topBarBackgroundButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.backgroundHeaderText, + ); + await runner.verifyElementText( + userProfilePage.backgroundHeaderText, + profilePageData.backgroundHeaderText, + ); + + // Click on Add Background + await runner.verifyElementIsVisible( + userProfilePage.backgroundPlusIconButton, + ); + await runner.clickOnElement(userProfilePage.backgroundPlusIconButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_CREATE_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.createBackgroundHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createBackgroundHeaderText, + profilePageData.createBackgroundHeaderText, + ); + + // Select Background Type + await runner.selectDropdownOptionByText( + userProfilePage.createBackgroundTypeButtonText, + backgroundType, + ); + + // Click on Submit Button + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_PAGE_URL, + ); + await runner.waitForMilliseconds(1000); + await runner.verifyElementIsVisible(userProfilePage.table); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyBackgroundCreateToast, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.clickOnElement(userProfilePage.toastCloseButton); + + // Click on Update Background + await runner.clickOnElement(userProfilePage.tableUpdateButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_UPDATE_PAGE_URL, + true, + ); + await runner.waitUntilElementIsInvisible( + userProfilePage.loadingRecordsText, + ); + await runner.verifyElementIsVisible( + "xpath=//h1[normalize-space()='Update Background']", + ); + await runner.verifyElementText( + "xpath=//h1[normalize-space()='Update Background']", + "Update Background", + ); + + // Add Role + await runner.enterText(userProfilePage.createBackgroundRoleInput, role); + // Add Department + await runner.enterText( + userProfilePage.createBackgroundDepartmentInput, + department, + ); + // Add Organization + await runner.enterText( + userProfilePage.createBackgroundOrganizationInput, + organization, + ); + // Add Organization Address + await runner.enterText( + userProfilePage.createBackgroundOrganizationAddressInput, + organizationAddress, + ); + // Add Start Year + await runner.enterText( + userProfilePage.createBackgroundStartYearInput, + startDate.year.toString(), + ); + // Add Start Month + await runner.enterText( + userProfilePage.createBackgroundStartMonthInput, + startDate.month.toString(), + ); + // Add Start Day + await runner.enterText( + userProfilePage.createBackgroundStartDayInput, + startDate.day.toString(), + ); + // Add End Year + await runner.enterText( + userProfilePage.createBackgroundEndYearInput, + endDate.year.toString(), + ); + // Add End Month + await runner.enterText( + userProfilePage.createBackgroundEndMonthInput, + endDate.month.toString(), + ); + // Add End Day + await runner.enterText( + userProfilePage.createBackgroundEndDayInput, + endDate.day.toString(), + ); + + // Click on Submit Button + await runner.clickOnElement(userProfilePage.submitButton); + await runner.waitForMilliseconds(1000); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_PAGE_URL, + true, + ); + await runner.verifyElementIsVisible(userProfilePage.table); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyBackgroundUpdateToast, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.clickOnElement(userProfilePage.toastCloseButton); + + // Remove Background + await runner.clickOnElement(userProfilePage.tableDeleteButton); + + // Open Background Delete Modal + await runner.verifyElementIsVisible( + userProfilePage.deleteModalHeaderText, + ); + await runner.verifyElementText( + userProfilePage.deleteModalHeaderText, + profilePageData.deleteModalHeaderText, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.deleteModalButton, + profilePageData.deleteModalDeleteButtonText, + ); + await runner.waitForMilliseconds(1000); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyBackgroundDeleteToast, + ); + }); + + test("Should Display Validation Error For Create Background Required Fields", async ({ + runner, + userProfilePage, + }) => { + // Click on user background tab + await runner.clickOnElement(userProfilePage.topBarBackgroundButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.backgroundHeaderText, + ); + await runner.verifyElementText( + userProfilePage.backgroundHeaderText, + profilePageData.backgroundHeaderText, + ); + + // Click on Add Background + await runner.verifyElementIsVisible( + userProfilePage.backgroundPlusIconButton, + ); + await runner.clickOnElement(userProfilePage.backgroundPlusIconButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_BACKGROUND_CREATE_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.createBackgroundHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createBackgroundHeaderText, + profilePageData.createBackgroundHeaderText, + ); + + // Click on submit + await runner.clickOnElement(userProfilePage.submitButton); + + await runner.verifyElementIsVisible( + userProfilePage.createBackgroundTypeHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createBackgroundTypeErrorText, + profilePageData.createBackgroundTypeErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.createBackgroundTypeErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.createBackgroundTypeHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + }); + + // 🔹 Works Tab – Test Case Scenarios + test("Validating The 'Works' Tab activated & No Research Work Data Exists", async ({ + runner, + userProfilePage, + }) => { + // Click on user Works tab + await runner.clickOnElement(userProfilePage.topBarWorksButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_WORK_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.worksHeaderText); + await runner.verifyElementText( + userProfilePage.worksHeaderText, + profilePageData.worksHeaderText, + ); + + // Other Tabs are not active when "Works" is selected. + await runner.validateAttribute( + [ + userProfilePage.topBarGeneralButton, + userProfilePage.topBarBackgroundButton, + userProfilePage.topBarProjectsButton, + userProfilePage.topBarAttachmentsButton, + ], + "data-state", + "inactive", + ); + + // Verify that the "No research works" message is displayed when there are no research work entries. + await runner.validateNoDataFound( + userProfilePage.noDataFoundTexts, + userProfilePage.table, + profilePageData.noWorksText, + ); + }); + + test("Validating Successfully Navigate To Research Work Creation Page", async ({ + runner, + userProfilePage, + }) => { + // Click on user Works tab + await runner.clickOnElement(userProfilePage.topBarWorksButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_WORK_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.worksHeaderText); + await runner.verifyElementText( + userProfilePage.worksHeaderText, + profilePageData.worksHeaderText, + ); + + // Click on Add Research Work + await runner.verifyElementIsVisible( + userProfilePage.worksPlusIconButton, + ); + await runner.clickOnElement(userProfilePage.worksPlusIconButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_WORK_CREATE_PAGE_URL, + ); + + await runner.verifyElementIsVisible([ + userProfilePage.createWorkHeaderText, + userProfilePage.backAndCancelButton, + userProfilePage.createWorkTypeHeaderText, + userProfilePage.createWorkJournalTitleHeaderText, + userProfilePage.createWorkTitleHeaderText, + userProfilePage.createWorkPublicationYearHeaderText, + userProfilePage.createWorkPublicationMonthHeaderText, + userProfilePage.createWorkPublicationDayHeaderText, + ]); + + await runner.verifyElementText( + userProfilePage.createWorkHeaderText, + profilePageData.createWorkHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createWorkTypeHeaderText, + profilePageData.createWorkTypeHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createWorkJournalTitleHeaderText, + profilePageData.createWorkJournalTitleHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createWorkTitleHeaderText, + profilePageData.createWorkTitleHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createWorkPublicationYearHeaderText, + profilePageData.createWorkPublicationYearHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createWorkPublicationMonthHeaderText, + profilePageData.createWorkPublicationMonthHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createWorkPublicationDayHeaderText, + profilePageData.createWorkPublicationDayHeaderText, + ); + + // Verify that the "Back" button is displayed and clickable. + await runner.validateTextAndClickOnElement( + userProfilePage.backAndCancelButton, + profilePageData.backButtonText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_WORK_PAGE_URL); + }); + + test("Validating Add Research Works Successfully & Remove Successfully", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click on user Works tab + await runner.clickOnElement(userProfilePage.topBarWorksButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_WORK_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.worksHeaderText); + await runner.verifyElementText( + userProfilePage.worksHeaderText, + profilePageData.worksHeaderText, + ); + + // Click on Add Research Work + await runner.verifyElementIsVisible( + userProfilePage.worksPlusIconButton, + ); + await runner.clickOnElement(userProfilePage.worksPlusIconButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_WORK_CREATE_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.createWorkHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createWorkHeaderText, + profilePageData.createWorkHeaderText, + ); + + // Select Work Type + await runner.selectDropdownOptionByText( + userProfilePage.createWorkTypeButtonText, + workType, + ); + + // Add Journal Title + await runner.enterText( + userProfilePage.createWorkJournalTitleInput, + journalTitle, + ); + // Add Title + await runner.enterText(userProfilePage.createWorkTitleInput, title); + // Add Publication Year + await runner.enterText( + userProfilePage.createWorkPublicationYearInput, + publicationYear.toString(), + ); + // Add Publication Month + await runner.enterText( + userProfilePage.createWorkPublicationMonthInput, + publicationMonth.toString(), + ); + // Add Publication Day + await runner.enterText( + userProfilePage.createWorkPublicationDayInput, + publicationDay.toString(), + ); + + // Click on Submit Button + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_WORK_PAGE_URL, + true, + ); + await runner.waitForMilliseconds(1000); + await runner.verifyElementIsVisible(userProfilePage.table); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyWorkCreateToast, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.clickOnElement(userProfilePage.toastCloseButton); + + await runner.verifyElementIsVisible(userProfilePage.sidebarWorksValue); + + // Remove Work + await runner.clickOnElement(userProfilePage.tableDeleteButton); + + // Open Work Delete Modal + await runner.verifyElementIsVisible( + userProfilePage.deleteModalHeaderText, + ); + await runner.verifyElementText( + userProfilePage.deleteModalHeaderText, + profilePageData.deleteModalHeaderText, + ); + await runner.validateTextAndClickOnElement( + userProfilePage.deleteModalButton, + profilePageData.deleteModalDeleteButtonText, + ); + await runner.waitForMilliseconds(1000); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyWorkDeleteToast, + ); + }); + + test("Validation Research Works Data Update Successfully & Remove Successfully", async ({ + runner, + userProfilePage, + userLoginPage, + }) => { + // Click on user Works tab + await runner.clickOnElement(userProfilePage.topBarWorksButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_WORK_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.worksHeaderText); + await runner.verifyElementText( + userProfilePage.worksHeaderText, + profilePageData.worksHeaderText, + ); + + // Click on Add Research Work + await runner.verifyElementIsVisible( + userProfilePage.worksPlusIconButton, + ); + await runner.clickOnElement(userProfilePage.worksPlusIconButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_WORK_CREATE_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.createWorkHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createWorkHeaderText, + profilePageData.createWorkHeaderText, + ); + + // Select Work Type + await runner.selectDropdownOptionByText( + userProfilePage.createWorkTypeButtonText, + workType, + ); + + // Add Journal Title + await runner.enterText( + userProfilePage.createWorkJournalTitleInput, + journalTitle, + ); + // Add Title + await runner.enterText(userProfilePage.createWorkTitleInput, title); + + // Click on Submit Button + await runner.validateTextAndClickOnElement( + userProfilePage.submitButton, + profilePageData.submitButtonText, + ); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_WORK_PAGE_URL, + true, + ); + await runner.validateElementInvisibility(userProfilePage.submitButton); + await runner.verifyElementIsVisible(userProfilePage.worksHeaderText); + await runner.verifyElementIsVisible(userProfilePage.table); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyWorkCreateToast, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.clickOnElement(userProfilePage.toastCloseButton); + + // Click on Update Work + await runner.clickOnElement(userProfilePage.tableUpdateButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_WORK_UPDATE_PAGE_URL, + true, + ); + await runner.waitUntilElementIsInvisible( + userProfilePage.loadingRecordsText, + ); + await runner.verifyElementIsVisible( + "xpath=//h1[normalize-space()='Update Research Work']", + ); + await runner.verifyElementText( + "xpath=//h1[normalize-space()='Update Research Work']", + "Update Research Work", + ); + + await runner.enterText( + userProfilePage.createWorkJournalTitleInput, + journalTitle, + ); + + // Add Publication Year + await runner.enterText( + userProfilePage.createWorkPublicationMonthInput, + publicationYear.toString(), + ); + // Add Publication Month + await runner.enterText( + userProfilePage.createWorkPublicationMonthInput, + publicationMonth.toString(), + ); + // Add Publication Day + await runner.enterText( + userProfilePage.createWorkPublicationDayInput, + publicationDay.toString(), + ); + + // Click on Submit Button + await runner.clickOnElement(userProfilePage.submitButton); + await runner.waitForMilliseconds(1000); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_WORK_PAGE_URL, + true, + ); + await runner.verifyElementIsVisible(userProfilePage.table); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.verifyElementText( + userLoginPage.toastMessage, + profilePageData.successfullyWorkUpdateToast, + ); + await runner.verifyElementIsVisible(userLoginPage.toastMessage); + await runner.hoverOnElement(userLoginPage.toastMessage); + await runner.clickOnElement(userProfilePage.toastCloseButton); + }); + + test("Validating The Select Works Type Combobox Values", async ({ + runner, + userProfilePage, + }) => { + // Click on user Works tab + await runner.clickOnElement(userProfilePage.topBarWorksButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_WORK_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.worksHeaderText); + await runner.verifyElementText( + userProfilePage.worksHeaderText, + profilePageData.worksHeaderText, + ); + + // Click on Add Research Work + await runner.verifyElementIsVisible( + userProfilePage.worksPlusIconButton, + ); + await runner.clickOnElement(userProfilePage.worksPlusIconButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_WORK_CREATE_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.createWorkHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createWorkHeaderText, + profilePageData.createWorkHeaderText, + ); + + // validating Work Type combobox values + await runner.selectAllMatchingDropdownOptions( + userProfilePage.createWorkTypeButtonText, + profilePageData.createWorkTypeDropdownTexts, + false, + ); + }); + + test("Validating Work Type Combobox Search Functionality", async ({ + runner, + userProfilePage, + }) => { + // Click on user Works tab + await runner.clickOnElement(userProfilePage.topBarWorksButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_WORK_PAGE_URL); + await runner.clickOnElement(userProfilePage.worksPlusIconButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_WORK_CREATE_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.createWorkHeaderText, + ); + + // Click to open the Work Type dropdown + await runner.clickOnElement(userProfilePage.createWorkTypeButtonText); + await runner.verifyElementIsVisible( + userProfilePage.createTypeSearchInput, + ); + await runner.validateAttribute( + userProfilePage.createTypeSearchInput, + "placeholder", + profilePageData.createWorkTypeSearchPlaceholderText, + ); + + // Scenario 1: Search with matching results + await runner.enterText( + userProfilePage.createTypeSearchInput, + workType?.replace(/[\s/-]/g, ""), // Remove spaces, slashes, and dashes + ); + await runner.verifyExpectedOptionInDropdown( + userProfilePage.createTypeSearchInput, + ); + + // Scenario 2: Search with no matching results + await runner.clearInputField(userProfilePage.createTypeSearchInput); + await runner.enterText( + userProfilePage.createTypeSearchInput, + randomCharValue30Digits, + ); + + // Verify that "No Results Found" message is displayed + await runner.verifyElementIsVisible( + userProfilePage.noResultsFoundMessage, + ); + await runner.verifyElementText( + userProfilePage.noResultsFoundMessage, + profilePageData.noResultsFoundMessage, + ); + + // Optionally, click outside or press escape to close the dropdown + await runner.pressKeyWithDelay("Escape"); + }); + + test("Should Display Validation Errors For Research Works Required Fields", async ({ + runner, + userProfilePage, + }) => { + // Click on user Works tab + await runner.clickOnElement(userProfilePage.topBarWorksButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_WORK_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.worksHeaderText); + await runner.verifyElementText( + userProfilePage.worksHeaderText, + profilePageData.worksHeaderText, + ); + + // Click on Add Research Work + await runner.verifyElementIsVisible( + userProfilePage.worksPlusIconButton, + ); + await runner.clickOnElement(userProfilePage.worksPlusIconButton); + await runner.verifyContainsUrl( + ENV.FUND_FIT_PROFILE_WORK_CREATE_PAGE_URL, + ); + await runner.verifyElementIsVisible( + userProfilePage.createWorkHeaderText, + ); + await runner.verifyElementText( + userProfilePage.createWorkHeaderText, + profilePageData.createWorkHeaderText, + ); + + // Click on Submit Button + await runner.clickOnElement(userProfilePage.submitButton); + await runner.verifyElementIsVisible([ + userProfilePage.createWorkTypeErrorText, + userProfilePage.createWorkJournalTitleErrorText, + userProfilePage.createWorkTitleErrorText, + ]); + + // Work Type Required + await runner.verifyElementText( + userProfilePage.createWorkTypeErrorText, + profilePageData.createWorkTypeErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.createWorkTypeErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.createWorkTypeHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + // Journal Title Required + await runner.verifyElementText( + userProfilePage.createWorkJournalTitleErrorText, + profilePageData.createWorkJournalTitleErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.createWorkJournalTitleErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.createWorkJournalTitleHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + // Title Required + await runner.verifyElementText( + userProfilePage.createWorkTitleErrorText, + profilePageData.createWorkTitleErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.createWorkTitleErrorText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userProfilePage.createWorkTitleHeaderText, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + }); + + // 🔹 Projects Tab – Test Case Scenarios + test("Validating The 'Projects' Tab activated & No Research Work Data Exists", async ({ + runner, + userProfilePage, + }) => { + // Click on user Projects tab + await runner.clickOnElement(userProfilePage.topBarProjectsButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_PROJECT_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.projectsHeaderText); + await runner.verifyElementText( + userProfilePage.projectsHeaderText, + profilePageData.projectHeaderText, + ); + + // Other Tabs are not active when "Projects" is selected. + await runner.validateAttribute( + [ + userProfilePage.topBarGeneralButton, + userProfilePage.topBarBackgroundButton, + userProfilePage.topBarWorksButton, + userProfilePage.topBarAttachmentsButton, + ], + "data-state", + "inactive", + ); + + // Verify that the "No research projects" message is displayed when there are no research work entries. + await runner.validateNoDataFound( + userProfilePage.noDataFoundTexts, + userProfilePage.table, + profilePageData.noProjectsText, + ); + }); + + test("Validating Successfully Navigate To Project Creation Page", async ({ + runner, + userProfilePage, + }) => { + // Click on user Projects tab + await runner.clickOnElement(userProfilePage.topBarProjectsButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_PROJECT_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.projectsHeaderText); + await runner.verifyElementText( + userProfilePage.projectsHeaderText, + profilePageData.projectHeaderText, + ); + + // Click on Add + await runner.verifyElementIsVisible( + userProfilePage.projectsPlusIconButton, + ); + await runner.clickOnElement(userProfilePage.projectsPlusIconButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_CREATE_PROJECT_PAGE_URL); + + await runner.waitUntilElementIsInvisible( + userProfilePage.projectsPlusIconButton, + ); + + await runner.verifyElementIsVisible( + userProfilePage.createProjectsHeaderTextSection, + ); + + await runner.verifyElementText( + userProfilePage.createProjectsHeaderTextSection, + profilePageData.createProjectFormHeaderText, + "h1", + ); + + await runner.verifyElementText( + userProfilePage.createProjectsHeaderTextSection, + profilePageData.backButtonText, + "a", + ); + + await runner.verifyElementIsVisible( + userProfilePage.selectProjectTypeComboBoxInput, + ); + + await runner.validateAttribute( + userProfilePage.selectProjectTypeComboBoxInput, + "data-state", + "closed", + ); + + await runner.clickOnElement( + userProfilePage.selectProjectTypeComboBoxInput, + ); + + await runner.validateAttribute( + userProfilePage.selectProjectTypeComboBoxInput, + "data-state", + "open", + ); + + await runner.selectDropdownOptionByText( + userProfilePage.selectProjectTypeComboBoxInput, + "contract", + ); + + await runner.validateAttribute( + userProfilePage.selectProjectTypeComboBoxInput, + "data-state", + "closed", + ); + }); + + test("Validating The Select Project Type Combobox Values", async ({ + runner, + userProfilePage, + }) => { + // Click on user Projects tab + await runner.clickOnElement(userProfilePage.topBarProjectsButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_PROFILE_PROJECT_PAGE_URL); + await runner.verifyElementIsVisible(userProfilePage.projectsHeaderText); + await runner.verifyElementText( + userProfilePage.projectsHeaderText, + profilePageData.projectHeaderText, + ); + + // Click on Add + await runner.verifyElementIsVisible( + userProfilePage.projectsPlusIconButton, + ); + await runner.clickOnElement(userProfilePage.projectsPlusIconButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_CREATE_PROJECT_PAGE_URL); + + await runner.waitUntilElementIsInvisible( + userProfilePage.projectsPlusIconButton, + ); + + await runner.verifyElementIsVisible( + userProfilePage.createProjectsHeaderTextSection, + ); + + await runner.verifyElementText( + userProfilePage.createProjectsHeaderTextSection, + profilePageData.createProjectFormHeaderText, + "h1", + ); + + await runner.verifyElementText( + userProfilePage.createProjectsHeaderTextSection, + profilePageData.backButtonText, + "a", + ); + + await runner.verifyElementIsVisible( + userProfilePage.selectProjectTypeComboBoxInput, + ); + + await runner.clickOnElement( + userProfilePage.selectProjectTypeComboBoxInput, + ); + + await runner.validateAttribute( + userProfilePage.selectProjectTypeComboBoxInput, + "data-state", + "open", + ); + + await runner.verifyLinksTexts( + userProfilePage.selectProjectTypeComboBoxTextValues, + profilePageData.comboBoxPlaceholderText, + ); + + await runner.selectDropdownOptionByText( + userProfilePage.selectProjectTypeComboBoxInput, + "contract", + ); + + await runner.validateAttribute( + userProfilePage.selectProjectTypeComboBoxInput, + "data-state", + "closed", + ); + + await runner.selectDropdownOptionByText( + userProfilePage.selectProjectTypeComboBoxInput, + "salary award", + ); + + await runner.selectDropdownOptionByText( + userProfilePage.selectProjectTypeComboBoxInput, + "grant", + ); + + await runner.selectDropdownOptionByText( + userProfilePage.selectProjectTypeComboBoxInput, + "award", + ); + + await runner.validateAttribute( + userProfilePage.selectProjectTypeComboBoxInput, + "data-state", + "closed", + ); + }); + }); // End of describe block + } // End of runTests method +} // End of UserProfileTest class +const testSuite = new UserProfileTest(); +testSuite.runTests(); diff --git a/tests/UserRegisterTest.spec.ts b/tests/UserRegisterTest.spec.ts new file mode 100644 index 0000000..a1d89c0 --- /dev/null +++ b/tests/UserRegisterTest.spec.ts @@ -0,0 +1,1012 @@ +import { test } from "../utilities/fixtures"; +import { ExpectedTextProvider } from "../utilities/valueProvider"; +import { ENV } from "../utilities/env"; +import { FakerDataProvider } from "../utilities/fakerDataProvider"; +import fundFitData from "../testData/fundFit.json"; +import homePageData from "../testData/homePageData.json"; +import registerPageData from "../testData/registerPageData.json"; + +class UserRegisterTest extends ExpectedTextProvider { + private fakerData: FakerDataProvider; + constructor() { + super(); + this.fakerData = new FakerDataProvider(); + } + + runTests() { + const { firstName, lastName, phoneNumber, email, password } = + this.fakerData; + + test.describe("Validating User Login Scenarios", () => { + test.beforeEach(async ({ runner, userLandingPage, userRegisterPage }) => { + await runner.navigateTo(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyContainsUrl(ENV.FUND_FIT_TEST_ENV_URL); + await runner.verifyPageTitle(fundFitData.fundFitTitle); + await runner.verifyElementIsVisible(userLandingPage.homePageHeroText); + await runner.verifyElementIsVisible(userLandingPage.registerButton); + + await runner.validateTextAndClickOnElement( + userLandingPage.registerButton, + homePageData.registerButtonText, + ); + + await runner.waitUntilElementIsInvisible( + userLandingPage.homePageHeroText, + ); + await runner.verifyContainsUrl(ENV.FUND_FIT_REGISTER_PAGE_URL); + await runner.verifyElementIsVisible(userRegisterPage.firstName); + }); + + test("Validating Register Form Contents", async ({ + runner, + userRegisterPage, + }) => { + await runner.verifyElementIsVisible(userRegisterPage.heroText); + await runner.verifyElementText( + userRegisterPage.heroText, + registerPageData.registerPageHeroText, + ); + + await runner.verifyElementText( + userRegisterPage.heroText2, + registerPageData.registerPageHeroText2, + ); + + await runner.validateAttribute( + userRegisterPage.heroText2, + "href", + ENV.FUND_FIT_LANDING_PAGE_URL, + "a", + ); + + await runner.verifyElementIsVisible(userRegisterPage.firstName); + await runner.verifyElementPlaceholder( + userRegisterPage.firstName, + registerPageData.registerPageFirstNamePlaceholderText, + ); + await runner.verifyElementIsVisible(userRegisterPage.lastName); + await runner.verifyElementPlaceholder( + userRegisterPage.lastName, + registerPageData.registerPageLastNamePlaceholderText, + ); + + await runner.verifyElementIsVisible(userRegisterPage.email); + await runner.verifyElementPlaceholder( + userRegisterPage.email, + registerPageData.registerPageEmailPlaceholderText, + ); + + await runner.verifyElementIsVisible(userRegisterPage.phoneNumber); + await runner.verifyElementPlaceholder( + userRegisterPage.phoneNumber, + registerPageData.registerPagePhonePlaceholderText, + ); + + await runner.verifyElementIsVisible(userRegisterPage.password); + await runner.verifyElementPlaceholder( + userRegisterPage.password, + registerPageData.registerPagePasswordPlaceholderText, + ); + + await runner.verifyElementIsVisible(userRegisterPage.confirmPassword); + await runner.verifyElementPlaceholder( + userRegisterPage.confirmPassword, + registerPageData.registerPageConfirmPasswordPlaceholderText, + ); + + await runner.verifyElementIsVisible(userRegisterPage.submitButton); + await runner.verifyElementText( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + + await runner.verifyElementIsVisible(userRegisterPage.privacyPolicyText); + await runner.verifyElementText( + userRegisterPage.privacyPolicyText, + registerPageData.registerPagePrivacyPolicyText, + ); + }); + + //Skipping this test case to avoid unwanted user creation + test.skip("User Completes & Submits A Registration", async ({ + runner, + userRegisterPage, + userLoginPage, + }) => { + await runner.verifyElementIsVisible(userRegisterPage.heroText); + await runner.verifyElementText( + userRegisterPage.heroText, + registerPageData.registerPageHeroText, + ); + + await runner.verifyElementText( + userRegisterPage.heroText2, + registerPageData.registerPageHeroText2, + ); + + await runner.validateAttribute( + userRegisterPage.heroText2, + "href", + ENV.FUND_FIT_LANDING_PAGE_URL, + "a", + ); + + await runner.verifyElementText( + userRegisterPage.firstNameHeader, + "First Name", + ); + + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.firstNameHeader, + fundFitData.textColorCss, + fundFitData.blackColorCode, + ); + + await runner.waitUntilElementIsInvisible( + userRegisterPage.firstNameErrorMessage, + ); + + await runner.verifyElementIsVisible(userRegisterPage.firstName); + await runner.verifyElementPlaceholder( + userRegisterPage.firstName, + registerPageData.registerPageFirstNamePlaceholderText, + ); + + await runner.clickOnElement(userRegisterPage.firstName); + await runner.enterText(userRegisterPage.firstName, firstName); + await runner.verifyInputValue(userRegisterPage.firstName, firstName); + + await runner.verifyElementIsVisible(userRegisterPage.lastName); + await runner.verifyElementPlaceholder( + userRegisterPage.lastName, + registerPageData.registerPageLastNamePlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.lastName); + await runner.enterText(userRegisterPage.lastName, lastName); + await runner.verifyInputValue(userRegisterPage.lastName, lastName); + + await runner.verifyElementIsVisible(userRegisterPage.email); + await runner.verifyElementPlaceholder( + userRegisterPage.email, + registerPageData.registerPageEmailPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.email); + await runner.enterText(userRegisterPage.email, email); + await runner.verifyInputValue(userRegisterPage.email, email); + + await runner.verifyElementIsVisible(userRegisterPage.phoneNumber); + await runner.verifyElementPlaceholder( + userRegisterPage.phoneNumber, + registerPageData.registerPagePhonePlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.phoneNumber); + await runner.enterText(userRegisterPage.phoneNumber, phoneNumber); + await runner.verifyInputValue( + userRegisterPage.phoneNumber, + phoneNumber, + ); + + await runner.verifyElementIsVisible(userRegisterPage.password); + await runner.verifyElementPlaceholder( + userRegisterPage.password, + registerPageData.registerPagePasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.password); + await runner.enterText(userRegisterPage.password, password); + await runner.verifyInputValue(userRegisterPage.password, password); + + await runner.verifyElementIsVisible(userRegisterPage.confirmPassword); + await runner.verifyElementPlaceholder( + userRegisterPage.confirmPassword, + registerPageData.registerPageConfirmPasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.confirmPassword); + await runner.enterText(userRegisterPage.confirmPassword, password); + await runner.verifyInputValue( + userRegisterPage.confirmPassword, + password, + ); + + await runner.verifyElementIsVisible(userRegisterPage.privacyPolicyText); + await runner.verifyElementText( + userRegisterPage.privacyPolicyText, + registerPageData.registerPagePrivacyPolicyText, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.privacyPolicyCheckbox, + ); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "unchecked", + ); + + await runner.clickOnElement(userRegisterPage.privacyPolicyCheckbox); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "checked", + ); + + await runner.verifyElementIsVisible(userRegisterPage.submitButton); + await runner.verifyElementText( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + + await runner.clickOnElement(userRegisterPage.submitButton); + + await runner.verifyElementIsVisible(userLoginPage.loginButton); + await runner.verifyContainsUrl(ENV.FUND_FIT_LOGIN_PAGE_URL); + }); + + test("User Register Form Should Not Be Submit-able Without First Name", async ({ + runner, + userRegisterPage, + }) => { + await runner.verifyElementIsVisible(userRegisterPage.firstName); + await runner.verifyElementPlaceholder( + userRegisterPage.firstName, + registerPageData.registerPageFirstNamePlaceholderText, + ); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.firstNameHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.lastName); + await runner.verifyElementPlaceholder( + userRegisterPage.lastName, + registerPageData.registerPageLastNamePlaceholderText, + ); + await runner.enterText(userRegisterPage.lastName, lastName); + await runner.verifyInputValue(userRegisterPage.lastName, lastName); + + await runner.verifyElementIsVisible(userRegisterPage.email); + await runner.verifyElementPlaceholder( + userRegisterPage.email, + registerPageData.registerPageEmailPlaceholderText, + ); + await runner.enterText(userRegisterPage.email, email); + await runner.verifyInputValue(userRegisterPage.email, email); + + await runner.verifyElementIsVisible(userRegisterPage.phoneNumber); + await runner.verifyElementPlaceholder( + userRegisterPage.phoneNumber, + registerPageData.registerPagePhonePlaceholderText, + ); + await runner.enterText(userRegisterPage.phoneNumber, phoneNumber); + await runner.verifyInputValue( + userRegisterPage.phoneNumber, + phoneNumber, + ); + + await runner.verifyElementIsVisible(userRegisterPage.password); + await runner.verifyElementPlaceholder( + userRegisterPage.password, + registerPageData.registerPagePasswordPlaceholderText, + ); + await runner.enterText(userRegisterPage.password, password); + await runner.verifyInputValue(userRegisterPage.password, password); + + await runner.verifyElementIsVisible(userRegisterPage.confirmPassword); + await runner.verifyElementPlaceholder( + userRegisterPage.confirmPassword, + registerPageData.registerPageConfirmPasswordPlaceholderText, + ); + await runner.enterText(userRegisterPage.confirmPassword, password); + await runner.verifyInputValue( + userRegisterPage.confirmPassword, + password, + ); + + await runner.verifyElementIsVisible(userRegisterPage.privacyPolicyText); + await runner.verifyElementText( + userRegisterPage.privacyPolicyText, + registerPageData.registerPagePrivacyPolicyText, + ); + await runner.verifyElementText( + userRegisterPage.privacyPolicyText, + registerPageData.registerPagePrivacyPolicyText, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.privacyPolicyCheckbox, + ); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "unchecked", + ); + await runner.clickOnElement(userRegisterPage.privacyPolicyCheckbox); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "checked", + ); + + await runner.verifyElementIsVisible(userRegisterPage.submitButton); + + await runner.validateTextAndClickOnElement( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.firstNameErrorMessage, + ); + await runner.verifyElementText( + userRegisterPage.firstNameErrorMessage, + registerPageData.registerPageFirstNameErrorText, + ); + + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.firstNameHeader, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.firstNameErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + }); + + test("User Register Form Should Not Be Submit-able Without Last Name", async ({ + runner, + userRegisterPage, + }) => { + await runner.verifyElementText( + userRegisterPage.heroText, + registerPageData.registerPageHeroText, + ); + + await runner.verifyElementText( + userRegisterPage.heroText2, + registerPageData.registerPageHeroText2, + ); + + await runner.verifyElementIsVisible(userRegisterPage.firstName); + await runner.verifyElementPlaceholder( + userRegisterPage.firstName, + registerPageData.registerPageFirstNamePlaceholderText, + ); + await runner.enterText(userRegisterPage.firstName, firstName); + await runner.verifyInputValue(userRegisterPage.firstName, firstName); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.firstNameHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.lastName); + await runner.verifyElementText( + userRegisterPage.lastNameHeader, + registerPageData.lastNameHeaderText, + ); + await runner.verifyElementPlaceholder( + userRegisterPage.lastName, + registerPageData.registerPageLastNamePlaceholderText, + ); + + await runner.verifyElementIsVisible(userRegisterPage.email); + await runner.verifyElementPlaceholder( + userRegisterPage.email, + registerPageData.registerPageEmailPlaceholderText, + ); + await runner.enterText(userRegisterPage.email, email); + await runner.verifyInputValue(userRegisterPage.email, email); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.email, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.phoneNumber); + await runner.verifyElementPlaceholder( + userRegisterPage.phoneNumber, + registerPageData.registerPagePhonePlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.phoneNumber); + await runner.enterText(userRegisterPage.phoneNumber, phoneNumber); + await runner.verifyInputValue( + userRegisterPage.phoneNumber, + phoneNumber, + ); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.phoneNumber, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.password); + await runner.verifyElementPlaceholder( + userRegisterPage.password, + registerPageData.registerPagePasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.password); + await runner.enterText(userRegisterPage.password, password); + await runner.verifyInputValue(userRegisterPage.password, password); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.password, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.confirmPassword); + await runner.verifyElementPlaceholder( + userRegisterPage.confirmPassword, + registerPageData.registerPageConfirmPasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.confirmPassword); + await runner.enterText(userRegisterPage.confirmPassword, password); + await runner.verifyInputValue( + userRegisterPage.confirmPassword, + password, + ); + + await runner.verifyElementIsVisible(userRegisterPage.privacyPolicyText); + await runner.verifyElementText( + userRegisterPage.privacyPolicyText, + registerPageData.registerPagePrivacyPolicyText, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.privacyPolicyCheckbox, + ); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "unchecked", + ); + await runner.clickOnElement(userRegisterPage.privacyPolicyCheckbox); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "checked", + ); + + await runner.verifyElementIsVisible(userRegisterPage.submitButton); + await runner.verifyElementText( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + await runner.clickOnElement(userRegisterPage.submitButton); + await runner.verifyElementIsVisible( + userRegisterPage.lastNameErrorMessage, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.lastNameErrorMessage, + ); + await runner.verifyElementText( + userRegisterPage.lastNameErrorMessage, + registerPageData.registerPageLastNameErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.lastNameErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + }); + + //Skipping this test case to avoid unwanted user creation + test.skip("User Register Form Should Not Be Submit-able Without Proper Email Format", async ({ + runner, + userRegisterPage, + userLoginPage, + }) => { + await runner.verifyElementText( + userRegisterPage.heroText, + registerPageData.registerPageHeroText, + ); + + await runner.verifyElementText( + userRegisterPage.heroText2, + registerPageData.registerPageHeroText2, + ); + + await runner.verifyElementIsVisible(userRegisterPage.firstName); + await runner.verifyElementPlaceholder( + userRegisterPage.firstName, + registerPageData.registerPageFirstNamePlaceholderText, + ); + await runner.enterText(userRegisterPage.firstName, firstName); + await runner.verifyInputValue(userRegisterPage.firstName, firstName); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.firstNameHeader, + fundFitData.textColorCss, + fundFitData.blackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.lastName); + await runner.verifyElementText( + userRegisterPage.lastNameHeader, + registerPageData.lastNameHeaderText, + ); + await runner.enterText(userRegisterPage.lastName, lastName); + await runner.verifyElementPlaceholder( + userRegisterPage.lastName, + registerPageData.registerPageLastNamePlaceholderText, + ); + + await runner.verifyElementIsVisible(userRegisterPage.phoneNumber); + await runner.verifyElementPlaceholder( + userRegisterPage.phoneNumber, + registerPageData.registerPagePhonePlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.phoneNumber); + await runner.enterText(userRegisterPage.phoneNumber, phoneNumber); + await runner.verifyInputValue( + userRegisterPage.phoneNumber, + phoneNumber, + ); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.phoneNumber, + fundFitData.textColorCss, + fundFitData.blackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.password); + await runner.verifyElementPlaceholder( + userRegisterPage.password, + registerPageData.registerPagePasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.password); + await runner.enterText(userRegisterPage.password, password); + await runner.verifyInputValue(userRegisterPage.password, password); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.password, + fundFitData.textColorCss, + fundFitData.blackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.confirmPassword); + await runner.verifyElementPlaceholder( + userRegisterPage.confirmPassword, + registerPageData.registerPageConfirmPasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.confirmPassword); + await runner.enterText(userRegisterPage.confirmPassword, password); + await runner.verifyInputValue( + userRegisterPage.confirmPassword, + password, + ); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.confirmPassword, + fundFitData.textColorCss, + fundFitData.blackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.privacyPolicyText); + await runner.verifyElementText( + userRegisterPage.privacyPolicyText, + registerPageData.registerPagePrivacyPolicyText, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.privacyPolicyCheckbox, + ); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "unchecked", + ); + await runner.clickOnElement(userRegisterPage.privacyPolicyCheckbox); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "checked", + ); + + //Email Block + await runner.verifyElementIsVisible(userRegisterPage.email); + await runner.verifyElementPlaceholder( + userRegisterPage.email, + registerPageData.registerPageEmailPlaceholderText, + ); + await runner.enterText(userRegisterPage.email, ""); + await runner.verifyInputValue(userRegisterPage.email, ""); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.email, + fundFitData.textColorCss, + fundFitData.blackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.submitButton); + await runner.verifyElementText( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + await runner.validateTextAndClickOnElement( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.emailInputBoxErrorMessage, + ); + await runner.verifyElementText( + userRegisterPage.emailInputBoxErrorMessage, + registerPageData.registerPageEmailErrorText, + ); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.emailInputBoxErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.enterText(userRegisterPage.email, "test"); + await runner.verifyInputValue(userRegisterPage.email, "test"); + + await runner.validateTextAndClickOnElement( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.emailInputBoxErrorMessage, + ); + await runner.verifyElementText( + userRegisterPage.emailInputBoxErrorMessage, + registerPageData.registerPageInvalidEmailErrorText, + ); + + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.emailInputBoxErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.enterText(userRegisterPage.email, "test@"); + await runner.verifyInputValue(userRegisterPage.email, "test@"); + + await runner.validateTextAndClickOnElement( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.emailInputBoxErrorMessage, + ); + await runner.verifyElementText( + userRegisterPage.emailInputBoxErrorMessage, + registerPageData.registerPageInvalidEmailErrorText, + ); + + await runner.enterText(userRegisterPage.email, "test@gmail"); + await runner.verifyInputValue(userRegisterPage.email, "test@gmail"); + + await runner.validateTextAndClickOnElement( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + + await runner.verifyElementIsVisible( + userRegisterPage.emailInputBoxErrorMessage, + ); + await runner.verifyElementText( + userRegisterPage.emailInputBoxErrorMessage, + registerPageData.registerPageInvalidEmailErrorText, + ); + + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.emailInputBoxErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + + await runner.enterText(userRegisterPage.email, email); + await runner.verifyInputValue(userRegisterPage.email, email); + + await runner.validateTextAndClickOnElement( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + + await runner.waitUntilElementIsInvisible( + userRegisterPage.emailInputBoxErrorMessage, + ); + + await runner.verifyElementIsVisible(userLoginPage.loginButton); + + await runner.verifyContainsUrl(ENV.FUND_FIT_LOGIN_PAGE_URL); + await runner.verifyElementIsVisible(userLoginPage.email); + + await runner.verifyElementIsVisible(userRegisterPage.toastMessage); + await runner.verifyElementText( + userRegisterPage.toastMessage, + "Registration SuccessfulPlease check your email for verification.", + ); + }); + + test("User Tries To Register User With Blank Phone Number", async ({ + runner, + userRegisterPage, + }) => { + await runner.verifyElementText( + userRegisterPage.heroText, + registerPageData.registerPageHeroText, + ); + + await runner.verifyElementText( + userRegisterPage.heroText2, + registerPageData.registerPageHeroText2, + ); + + await runner.verifyElementIsVisible(userRegisterPage.firstName); + await runner.verifyElementPlaceholder( + userRegisterPage.firstName, + registerPageData.registerPageFirstNamePlaceholderText, + ); + await runner.enterText(userRegisterPage.firstName, firstName); + await runner.verifyInputValue(userRegisterPage.firstName, firstName); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.firstNameHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.lastName); + await runner.verifyElementText( + userRegisterPage.lastNameHeader, + registerPageData.lastNameHeaderText, + ); + await runner.verifyElementPlaceholder( + userRegisterPage.lastName, + registerPageData.registerPageLastNamePlaceholderText, + ); + await runner.enterText(userRegisterPage.lastName, lastName); + await runner.verifyInputValue(userRegisterPage.lastName, lastName); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.lastNameHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.email); + await runner.verifyElementPlaceholder( + userRegisterPage.email, + registerPageData.registerPageEmailPlaceholderText, + ); + await runner.enterText(userRegisterPage.email, email); + await runner.verifyInputValue(userRegisterPage.email, email); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.email, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.phoneNumber); + await runner.verifyElementPlaceholder( + userRegisterPage.phoneNumber, + registerPageData.registerPagePhonePlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.phoneNumber); + await runner.enterText(userRegisterPage.phoneNumber, ""); + await runner.verifyInputValue(userRegisterPage.phoneNumber, ""); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.phoneNumber, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.password); + await runner.verifyElementPlaceholder( + userRegisterPage.password, + registerPageData.registerPagePasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.password); + await runner.enterText(userRegisterPage.password, password); + await runner.verifyInputValue(userRegisterPage.password, password); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.password, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.confirmPassword); + await runner.verifyElementPlaceholder( + userRegisterPage.confirmPassword, + registerPageData.registerPageConfirmPasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.confirmPassword); + await runner.enterText(userRegisterPage.confirmPassword, password); + await runner.verifyInputValue( + userRegisterPage.confirmPassword, + password, + ); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.confirmPassword, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.privacyPolicyText); + await runner.verifyElementText( + userRegisterPage.privacyPolicyText, + registerPageData.registerPagePrivacyPolicyText, + ); + await runner.verifyElementIsVisible( + userRegisterPage.privacyPolicyCheckbox, + ); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "unchecked", + ); + await runner.clickOnElement(userRegisterPage.privacyPolicyCheckbox); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "checked", + ); + + await runner.verifyElementIsVisible(userRegisterPage.submitButton); + await runner.verifyElementText( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + await runner.clickOnElement(userRegisterPage.submitButton); + + await runner.waitUntilElementIsInvisible( + userRegisterPage.phoneNumberInputBoxErrorMessage, + ); + + await runner.verifyContainsUrl(ENV.FUND_FIT_LOGIN_PAGE_URL); + await runner.verifyElementIsVisible(userRegisterPage.toastMessage); + await runner.verifyElementText( + userRegisterPage.toastMessage, + "Registration SuccessfulPlease check your email for verification.", + ); + }); + + test("User Tries To Register With Alphabetic Value In Phone Number", async ({ + runner, + userRegisterPage, + }) => { + await runner.verifyElementText( + userRegisterPage.heroText, + registerPageData.registerPageHeroText, + ); + + await runner.verifyElementText( + userRegisterPage.heroText2, + registerPageData.registerPageHeroText2, + ); + + await runner.verifyElementIsVisible(userRegisterPage.firstName); + await runner.verifyElementPlaceholder( + userRegisterPage.firstName, + registerPageData.registerPageFirstNamePlaceholderText, + ); + await runner.enterText(userRegisterPage.firstName, firstName); + await runner.verifyInputValue(userRegisterPage.firstName, firstName); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.firstNameHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.lastName); + await runner.verifyElementText( + userRegisterPage.lastNameHeader, + registerPageData.lastNameHeaderText, + ); + await runner.verifyElementPlaceholder( + userRegisterPage.lastName, + registerPageData.registerPageLastNamePlaceholderText, + ); + await runner.enterText(userRegisterPage.lastName, lastName); + await runner.verifyInputValue(userRegisterPage.lastName, lastName); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.lastNameHeader, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.email); + await runner.verifyElementPlaceholder( + userRegisterPage.email, + registerPageData.registerPageEmailPlaceholderText, + ); + await runner.enterText(userRegisterPage.email, email); + await runner.verifyInputValue(userRegisterPage.email, email); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.email, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.phoneNumber); + await runner.verifyElementPlaceholder( + userRegisterPage.phoneNumber, + registerPageData.registerPagePhonePlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.phoneNumber); + await runner.enterText(userRegisterPage.phoneNumber, "test"); + await runner.verifyInputValue(userRegisterPage.phoneNumber, "test"); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.phoneNumber, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.password); + await runner.verifyElementPlaceholder( + userRegisterPage.password, + registerPageData.registerPagePasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.password); + await runner.enterText(userRegisterPage.password, password); + await runner.verifyInputValue(userRegisterPage.password, password); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.password, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.confirmPassword); + await runner.verifyElementPlaceholder( + userRegisterPage.confirmPassword, + registerPageData.registerPageConfirmPasswordPlaceholderText, + ); + await runner.clickOnElement(userRegisterPage.confirmPassword); + await runner.enterText(userRegisterPage.confirmPassword, password); + await runner.verifyInputValue( + userRegisterPage.confirmPassword, + password, + ); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.confirmPassword, + fundFitData.textColorCss, + fundFitData.secondBlackColorCode, + ); + + await runner.verifyElementIsVisible(userRegisterPage.privacyPolicyText); + await runner.verifyElementText( + userRegisterPage.privacyPolicyText, + registerPageData.registerPagePrivacyPolicyText, + ); + await runner.verifyElementIsVisible( + userRegisterPage.privacyPolicyCheckbox, + ); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "unchecked", + ); + await runner.clickOnElement(userRegisterPage.privacyPolicyCheckbox); + await runner.verifyCheckboxDataState( + userRegisterPage.privacyPolicyCheckbox, + "checked", + ); + + await runner.verifyElementIsVisible(userRegisterPage.submitButton); + await runner.verifyElementText( + userRegisterPage.submitButton, + fundFitData.submitButtonText, + ); + await runner.clickOnElement(userRegisterPage.submitButton); + + await runner.verifyElementIsVisible( + userRegisterPage.phoneNumberInputBoxErrorMessage, + ); + await runner.verifyElementText( + userRegisterPage.phoneNumberInputBoxErrorMessage, + registerPageData.registerPageInvalidPhoneNumberErrorText, + ); + + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.phoneNumberHeader, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + await runner.verifyElementToHaveCSSProperty( + userRegisterPage.phoneNumberInputBoxErrorMessage, + fundFitData.textColorCss, + fundFitData.redColorCode, + ); + }); + + test("User Tries To Register With Wrong Password Combination", async ({ + runner, + userRegisterPage, + }) => {}); + }); //Describe block + } //End of runTests method +} //End of UserRegisterTest class +const testSuite = new UserRegisterTest(); +testSuite.runTests(); diff --git a/tests/lambdaTestPlayground.spec.ts b/tests/lambdaTestPlayground.spec.ts deleted file mode 100644 index d8d6c12..0000000 --- a/tests/lambdaTestPlayground.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { test } from "../utilities/fixtures"; -import lambdaData from "../testData/lambda.json"; -import { ExpectedTextProvider } from "../utilities/valueProvider"; - -class MenuValidationTests extends ExpectedTextProvider { - constructor() { - super(); - } - - runTests() { - test.describe.skip("Validating Menu Click", () => { - test.beforeEach(async ({ runner }) => { - await runner.navigateTo(lambdaData.lambdaTestUrl); - }); - - test("Validating Navigation with URL & Title", async ({ runner }) => { - await runner.verifyContainsUrl(lambdaData.lambdaTestUrl); - await runner.verifyTitle(lambdaData.pageTitle); - }); - - test("Validating and Entering value to text input field", async ({ - runner, - lambdaPage, - }) => { - await runner.typeInputBox(lambdaPage.textInputField, "cameras"); - await runner.clickOnElement(lambdaPage.searchButton); - await runner.mouseHover(lambdaPage.accountButton); - await runner.clickOnElement(lambdaPage.loginButton); - await runner.clickOnElement(lambdaPage.accountButton); - }); - - test("Validating Login Form & Successful Login Attempt", async ({ - runner, - lambdaPage, - }) => { - await runner.mouseHover(lambdaPage.accountButton); - await runner.clickOnElement(lambdaPage.loginButton); - await runner.clickOnElement(lambdaPage.accountButton); - - await runner.verifyLinksText( - lambdaPage.rightColumnList, - this.expectedTexts, - ); - }); - }); - } -} - -// Run the tests -const testSuite = new MenuValidationTests(); -testSuite.runTests(); diff --git a/tests/loginTest.spec.ts b/tests/loginTest.spec.ts deleted file mode 100644 index dbb681d..0000000 --- a/tests/loginTest.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { test } from "../utilities/fixtures"; -import lambdaData from "../testData/lambda.json"; -import { ExpectedTextProvider } from "../utilities/valueProvider"; - -class LoginTest extends ExpectedTextProvider { - constructor() { - super(); - } - - runTests() { - test.describe("Validating User Login Scenarios", () => { - test.beforeEach(async ({ runner }) => { - await runner.navigateTo(lambdaData.lambdaTestUrl); - await runner.verifyContainsUrl(lambdaData.lambdaTestUrl); - await runner.verifyTitle(lambdaData.pageTitle); - }); - - test.skip("Validating the input fields", async ({ - runner, - loginPage, - lambdaPage, - }) => { - await runner.mouseHover(lambdaPage.accountButton); - await runner.clickOnElement(lambdaPage.loginButton); - await runner.verifyContainsUrl(lambdaData.loginPageUrl); - await runner.verifyElementIsVisible(loginPage.emailInputField); - await runner.clearInputField(loginPage.emailInputField); - await runner.typeInputBox( - loginPage.emailInputField, - "testUser@gmail.com", - ); - await runner.verifyElementIsVisible(loginPage.passwordInputField); - await runner.clearInputField(loginPage.passwordInputField); - await runner.typeInputBox(loginPage.passwordInputField, "12345678"); - await runner.clickOnElement(loginPage.loginButton); - }); - - test("Validating Login Attempts With Valid Credentials", async ({ - runner, - loginPage, - lambdaPage, - accountPage, - }) => { - await runner.mouseHover(lambdaPage.accountButton); - await runner.clickOnElement(lambdaPage.loginButton); - await runner.verifyContainsUrl(lambdaData.loginPageUrl); - await runner.verifyElementIsVisible(loginPage.emailInputField); - await runner.clearInputField(loginPage.emailInputField); - await runner.typeInputBox( - loginPage.emailInputField, - "mahbubasr091@gmail.com", - ); - await runner.verifyElementIsVisible(loginPage.passwordInputField); - await runner.clearInputField(loginPage.passwordInputField); - await runner.typeInputBox(loginPage.passwordInputField, "1234567889"); - await runner.clickOnElement(loginPage.loginButton); - await runner.verifyElementIsVisible(accountPage.loginPageText); - await runner.verifyContainText( - accountPage.loginPageText, - "This is a dummy website for Web Automation Testing", - ); - await runner.verifyContainsUrl(lambdaData.accountPageUrl); - await runner.mouseHover(lambdaPage.accountButton); - await runner.clickOnElement(lambdaPage.accountButton); - }); - }); //exit - } -} -const testSuite = new LoginTest(); -testSuite.runTests(); diff --git a/utilities/env.ts b/utilities/env.ts new file mode 100644 index 0000000..2d61ca0 --- /dev/null +++ b/utilities/env.ts @@ -0,0 +1,83 @@ +import * as dotenv from "dotenv"; +import { z } from "zod"; + +dotenv.config(); + +const EnvSchema = z.object({ + FUND_FIT_TEST_ENV_URL: z.string().url(), + FUND_FIT_USER_EMAIL: z.string().email(), + FUND_FIT_USER_NEW_EMAIL: z.string().email(), + FUND_FIT_USER_PASSWORD: z.string().min(8), + FUND_FIT_USER_PREVIOUS_PASSWORD: z.string().min(8), + FUND_FIT_TEST_PASSWORD: z.string().min(8), + FUND_FIT_LOGIN_PAGE_URL: z.string().url(), + FUND_FIT_REGISTER_PAGE_URL: z.string().url(), + FUND_FIT_LANDING_PAGE_URL: z.string().url(), + FUND_FIT_FORGET_PASSWORD_PAGE_URL: z.string().url(), + FUND_FIT_DASHBOARD_PAGE_URL: z.string().url(), + FUND_FIT_START_MATCHING_PAGE_URL: z.string().url(), + FUND_FIT_MATCHES_PAGE_URL: z.string().url(), + FUND_FIT_NOTIFICATIONS_PAGE_URL: z.string().url(), + + FUND_FIT_PROFILE_PAGE_URL: z.string().url(), + FUND_FIT_PROFILE_EDIT_PAGE_URL: z.string().url(), + FUND_FIT_PROFILE_PASSWORD_SETTING_PAGE_URL: z.string().url(), + FUND_FIT_PROFILE_EMAIL_SETTING_PAGE_URL: z.string().url(), + + FUND_FIT_PROFILE_BACKGROUND_PAGE_URL: z.string().url(), + FUND_FIT_PROFILE_BACKGROUND_CREATE_PAGE_URL: z.string().url(), + FUND_FIT_PROFILE_BACKGROUND_UPDATE_PAGE_URL: z.string().url(), + + FUND_FIT_PROFILE_WORK_PAGE_URL: z.string().url(), + FUND_FIT_PROFILE_WORK_CREATE_PAGE_URL: z.string().url(), + FUND_FIT_PROFILE_WORK_UPDATE_PAGE_URL: z.string().url(), + + FUND_FIT_PROFILE_PROJECT_PAGE_URL: z.string().url(), + FUND_FIT_PROFILE_ATTACHMENT_PAGE_URL: z.string().url(), + FUND_FIT_CREATE_PROJECT_PAGE_URL: z.string().url(), +}); + +export const ENV = EnvSchema.parse({ + FUND_FIT_TEST_ENV_URL: process.env.FUND_FIT_TEST_ENV_URL, + FUND_FIT_USER_EMAIL: process.env.FUND_FIT_USER_EMAIL, + FUND_FIT_USER_NEW_EMAIL: process.env.FUND_FIT_USER_NEW_EMAIL, + FUND_FIT_USER_PASSWORD: process.env.FUND_FIT_USER_PASSWORD, + FUND_FIT_USER_PREVIOUS_PASSWORD: process.env.FUND_FIT_USER_PREVIOUS_PASSWORD, + FUND_FIT_TEST_PASSWORD: process.env.FUND_FIT_TEST_PASSWORD, + FUND_FIT_LOGIN_PAGE_URL: process.env.FUND_FIT_LOGIN_PAGE_URL, + FUND_FIT_REGISTER_PAGE_URL: process.env.FUND_FIT_REGISTER_PAGE_URL, + FUND_FIT_LANDING_PAGE_URL: process.env.FUND_FIT_LANDING_PAGE_URL, + FUND_FIT_FORGET_PASSWORD_PAGE_URL: + process.env.FUND_FIT_FORGET_PASSWORD_PAGE_URL, + FUND_FIT_DASHBOARD_PAGE_URL: process.env.FUND_FIT_DASHBOARD_PAGE_URL, + FUND_FIT_START_MATCHING_PAGE_URL: + process.env.FUND_FIT_START_MATCHING_PAGE_URL, + FUND_FIT_MATCHES_PAGE_URL: process.env.FUND_FIT_MATCHES_PAGE_URL, + FUND_FIT_NOTIFICATIONS_PAGE_URL: process.env.FUND_FIT_NOTIFICATIONS_PAGE_URL, + + FUND_FIT_PROFILE_PAGE_URL: process.env.FUND_FIT_PROFILE_PAGE_URL, + FUND_FIT_PROFILE_EDIT_PAGE_URL: process.env.FUND_FIT_PROFILE_EDIT_PAGE_URL, + FUND_FIT_PROFILE_PASSWORD_SETTING_PAGE_URL: + process.env.FUND_FIT_PROFILE_PASSWORD_SETTING_PAGE_URL, + FUND_FIT_PROFILE_EMAIL_SETTING_PAGE_URL: + process.env.FUND_FIT_PROFILE_EMAIL_SETTING_PAGE_URL, + + FUND_FIT_PROFILE_BACKGROUND_PAGE_URL: + process.env.FUND_FIT_PROFILE_BACKGROUND_PAGE_URL, + FUND_FIT_PROFILE_BACKGROUND_CREATE_PAGE_URL: + process.env.FUND_FIT_PROFILE_BACKGROUND_CREATE_PAGE_URL, + FUND_FIT_PROFILE_BACKGROUND_UPDATE_PAGE_URL: + process.env.FUND_FIT_PROFILE_BACKGROUND_UPDATE_PAGE_URL, + + FUND_FIT_PROFILE_WORK_PAGE_URL: process.env.FUND_FIT_PROFILE_WORK_PAGE_URL, + FUND_FIT_PROFILE_WORK_CREATE_PAGE_URL: + process.env.FUND_FIT_PROFILE_WORK_CREATE_PAGE_URL, + FUND_FIT_PROFILE_WORK_UPDATE_PAGE_URL: + process.env.FUND_FIT_PROFILE_WORK_UPDATE_PAGE_URL, + + FUND_FIT_PROFILE_PROJECT_PAGE_URL: + process.env.FUND_FIT_PROFILE_PROJECT_PAGE_URL, + FUND_FIT_PROFILE_ATTACHMENT_PAGE_URL: + process.env.FUND_FIT_PROFILE_ATTACHMENT_PAGE_URL, + FUND_FIT_CREATE_PROJECT_PAGE_URL: process.env.FUND_FIT_CREATE_PROJECT_PAGE_URL, +}); diff --git a/utilities/fakerDataProvider.ts b/utilities/fakerDataProvider.ts new file mode 100644 index 0000000..ec44cb6 --- /dev/null +++ b/utilities/fakerDataProvider.ts @@ -0,0 +1,150 @@ +import { faker } from "@faker-js/faker"; + +export class FakerDataProvider { + firstName: string; + lastName: string; + email: string; + phoneNumber: string; + password: string; + bio: string; + randomCharValue30Digits: string; + randomCharValuesForInputValueValidation: string; + numericValueOf20Digits: string; + backgroundType: (typeof backgroundTypes)[number]; + role: string; + department: string; + organization: string; + organizationAddress: string; + startDate: { + year: number; + month: number; + day: number; + }; + endDate: { + year: number; + month: number; + day: number; + }; + + workType: (typeof workTypes)[number]; + journalTitle: string; + title: string; + publicationYear: number; + publicationMonth: number; + publicationDay: number; + + projectTypes: (typeof projectTypes)[number]; + projectTitle: string; + organizationType: string + amount: string; + + constructor() { + this.firstName = faker.person.firstName(); + this.lastName = faker.person.lastName(); + this.email = faker.internet.email(); + this.phoneNumber = faker.string.numeric(10); + this.password = faker.internet.password(); + this.bio = faker.lorem.paragraphs(2); + this.randomCharValue30Digits = faker.string.alphanumeric(30); + this.randomCharValuesForInputValueValidation = + faker.string.alphanumeric(70); + this.numericValueOf20Digits = faker.string.numeric(20); + + this.backgroundType = faker.helpers.arrayElement(backgroundTypes); + this.role = faker.person.jobTitle(); + this.department = faker.commerce.department(); + this.organization = faker.company.name(); + this.organizationAddress = faker.location.streetAddress(); + + const start = faker.date.past({ years: 10 }); + const end = faker.date.recent({ days: 30 }); + + this.startDate = { + year: start.getFullYear(), + month: start.getMonth() + 1, + day: start.getDate(), + }; + + this.endDate = { + year: end.getFullYear(), + month: end.getMonth() + 1, + day: end.getDate(), + }; + + this.workType = faker.helpers.arrayElement(workTypes); + this.journalTitle = faker.company.name() + " Journal"; + this.title = faker.lorem.sentence(); + + const publicationDate = faker.date.past({ years: 5 }); + this.publicationYear = publicationDate.getFullYear(); + this.publicationMonth = publicationDate.getMonth() + 1; + this.publicationDay = publicationDate.getDate(); + + this.projectTypes = faker.helpers.arrayElement(projectTypes); + this.projectTitle= faker.company.catchPhrase(); + this.organizationType=faker.company.buzzVerb(); + this.amount = faker.string.numeric({ length: 5, allowLeadingZeros: false }); + } +} + +const backgroundTypes = [ + "Distinction", + "Education", + "Employment", + "Invited Position", + "Membership", + "Qualification", + "Service", +] as const; + +const workTypes = [ + "Annotation", + "Artistic Performance", + "Conference Abstract", + "Conference Paper", + "Conference Poster", + "Data Management Plan", + "Data Set", + "Dictionary Entry", + "Disclosure", + "Dissertation/Thesis", + "Edited Book", + "Encyclopedia Entry", + "Invention", + "Book", + "Book Chapter", + "Book Review", + "Edited Book", + "Encyclopedia Entry", + "Invention", + "Journal Article", + "Journal Issue", + "Lecture/Speech", + "License", + "Magazine Article", + "Manual", + "Newsletter Article", + "Newspaper Article", + "Online Resource", + "Other", + "Patent", + "Physical Object", + "Preprint", + "Registered Copyright", + "Report", + "Research Technique", + "Research Tool", + "Review", + "Software", + "Spin-off Company", + "Standards and Policy", + "Supervised Student Publication", + "Technical Standard", + "Test", + "Trademark", + "Translation", + "Website", + "Working Paper", +] as const; + +const projectTypes = ["Contract", "Salary Award", "Grant", "Award"] as const; diff --git a/utilities/fixtures.ts b/utilities/fixtures.ts index 3ad7c75..c0586fa 100644 --- a/utilities/fixtures.ts +++ b/utilities/fixtures.ts @@ -1,31 +1,72 @@ import { test as base, Page } from "@playwright/test"; -import { LambdaHomePage } from "../pom/lambdaHomePage"; -import { Utils } from "./utils"; -import { LoginPage } from "../pom/LoginPage"; -import { AccountPage } from "../pom/accountPage"; +import { Utils } from "./utility"; +import { loginPage } from "../pageObjectModel/loginPage"; +import { homePage } from "../pageObjectModel/homePage"; +import { registerPage } from "../pageObjectModel/registerPage"; +import { dashboardPage } from "../pageObjectModel/dashboardPage"; +import { startMatchingPage } from "../pageObjectModel/startMatchingPage"; +import { matchesPage } from "../pageObjectModel/matchesPage"; +import { notificationPage } from "../pageObjectModel/notificationPage"; +import { profilePage } from "../pageObjectModel/profilePage"; -const test = base.extend<{ +interface TestFixtures { runner: Utils; - lambdaPage: LambdaHomePage; - loginPage: LoginPage; - accountPage: AccountPage; -}>({ - runner: async ({ page }: { page: Page }, use) => { + userLoginPage: loginPage; + userLandingPage: homePage; + userRegisterPage: registerPage; + userDashboardPage: dashboardPage; + userStartMatchingPage: startMatchingPage; + userMatchesPage: matchesPage; + userNotificationPage: notificationPage; + userProfilePage: profilePage; +} + +interface PageFixture { + page: Page; +} + +const test = base.extend