init commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
src/framework/config/BBConfig.ts
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"env": {
|
||||
"commonjs": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"airbnb-base",
|
||||
"airbnb-typescript/base",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:playwright/playwright-test"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json",
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"indent": "off",
|
||||
"@typescript-eslint/indent": "off",
|
||||
"linebreak-style": "off",
|
||||
"quotes": "off",
|
||||
"no-trailing-spaces": "off",
|
||||
"lines-between-class-members": "off",
|
||||
"@typescript-eslint/lines-between-class-members": "off",
|
||||
"@typescript-eslint/quotes": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"object-shorthand": "off",
|
||||
"no-console":"off",
|
||||
"class-methods-use-this": "off",
|
||||
"no-plusplus": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"no-await-in-loop": "off",
|
||||
"function-paren-newline": "off",
|
||||
"function-call-argument-newline": "off",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"max-len": ["warn",
|
||||
{
|
||||
"code": 120 ,
|
||||
"ignoreComments": true,
|
||||
"ignoreTrailingComments": true,
|
||||
"ignoreTemplateLiterals": true,
|
||||
"ignoreUrls": true
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Automation Test Execution
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Test Execution
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code from repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setting up Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
- name: Install dependencies
|
||||
run: npm ci && npx playwright install
|
||||
- name: Creating test suite
|
||||
run: npm run create:suite SHEET=Regression --if-present
|
||||
- name: Test execution
|
||||
run: npm test
|
||||
- name: Generating execution report
|
||||
if: always()
|
||||
run: npx ts-node ./src/framework/reporter/HTMLReporter.ts
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results
|
||||
- name: Upload allure-results artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: allure-results
|
||||
path: allure-results
|
||||
retention-days: 30
|
||||
|
||||
generate_report:
|
||||
name: Allure Report
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
needs: [ tests ]
|
||||
steps:
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
id: download
|
||||
with:
|
||||
name: allure-results
|
||||
path: allure-results
|
||||
|
||||
- name: Get Allure history
|
||||
uses: actions/checkout@v4
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
with:
|
||||
ref: gh-pages
|
||||
path: gh-pages
|
||||
|
||||
- name: Allure Report action
|
||||
uses: simple-elf/allure-report-action@master
|
||||
if: always()
|
||||
id: allure-report
|
||||
with:
|
||||
allure_results: allure-results
|
||||
gh_pages: gh-pages
|
||||
allure_report: allure-report
|
||||
allure_history: allure-history
|
||||
|
||||
- name: Deploy allure report to Github Pages
|
||||
if: always()
|
||||
uses: peaceiris/actions-gh-pages@v2
|
||||
env:
|
||||
PERSONAL_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PUBLISH_BRANCH: gh-pages
|
||||
PUBLISH_DIR: allure-history
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
### Intellij+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Intellij+all Patch ###
|
||||
# Ignore everything but code style settings and run configurations
|
||||
# that are supposed to be shared within teams.
|
||||
|
||||
.idea/*
|
||||
|
||||
!.idea/codeStyles
|
||||
!.idea/runConfigurations
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
@@ -1,2 +1,95 @@
|
||||
# playwright-demo
|
||||
# Playwright-Demo
|
||||
|
||||
## **Overview:**
|
||||
|
||||
This is a sample Automation project using Playwright and Typescript and uses playwright-testrunner to execute test cases. This is a Data Driven framework focused on separating the test scripts logic and the test data from each other. This allows us to create test automation scripts by passing different sets of test data. The test data set is kept in an external Excel Sheet. The test scripts connect to the external Excel sheet to get the test data. This framework significantly reduces the number of test scripts compared to a modular based framework when we need to test for multiple sets of data for same functionality.
|
||||
|
||||
For Demo purpose UI test cases are created on [advantageonlineshopping.com](http://advantageonlineshopping.com/) site and API test cases are created on these [SOAP API](https://www.advantageonlineshopping.com/accountservice/ws/accountservice.wsdl) & [REST API](https://fakestoreapi.com) endpoints.
|
||||
|
||||
## Features
|
||||
|
||||
- This framework has built in library to operate on UI, API (both SOAP & REST API) and DB (MSSQL, DB2 & Oracle).
|
||||
- Supports execution of tests in different browsers.
|
||||
- Test data is stored in an Excel sheet and from this Excel sheet user can control the test cases that needs to be run.
|
||||
- User also has full control to run test in different modes from the Excel sheet.
|
||||
- Allows transfer of data between test cases.
|
||||
- Has utility built in for file download, Read PDF files etc.
|
||||
- Generates Playwright's HTML Report, Allure Report & JUnit Report in HTML format for each exaction.
|
||||
- Allure & Playwright report including snapshots and video in case of test failure.
|
||||
- Test execution logs are captured in the log file.
|
||||
- You Can execute local tests in Playwright's UI Mode, that comes with a built-in watch mode. Which helps in running and debuging of tests.
|
||||
- All the playwright related config is controlled by playwright config file.
|
||||
- Environment variables can be modified at runtime and its controlled by .env file.
|
||||
- Easy and simple integration to CI/CD tools like Jenkins.
|
||||
|
||||
#### Supported Browsers
|
||||
1. Chrome - default browser
|
||||
2. Firefox
|
||||
3. MS Edge
|
||||
4. WebKit - web browser engine used by Safari
|
||||
|
||||
#### Run Mode Details
|
||||
| Mode | Execl Value |Description |
|
||||
| ------ | ------ | ------ |
|
||||
|Normal|Blank| Runs the tests sequentially|
|
||||
|Serial|serial| Runs the tests sequentially. On test failure, all subsequent tests are skipped|
|
||||
|Parallel|parallel| Runs the tests parallelly, this is ideal when tests in the scenario are independent of one another|
|
||||
|
||||
#### Steps to use
|
||||
##### 1. Installation
|
||||
|
||||
Playwright framework requires [Node.js](https://nodejs.org/) v14+ to run.
|
||||
|
||||
Code from github need to be [download](https://github.com/VinayKumarBM/playwright-sample-project/archive/refs/heads/master.zip) OR [cloned](https://github.com/VinayKumarBM/playwright-sample-project.git) using git command.
|
||||
|
||||
Installing the dependencies.
|
||||
```sh
|
||||
npm ci
|
||||
```
|
||||
##### 2. Test creation
|
||||
- Create Test file with extenstion .spec.ts. Eg LoginTest.spec.ts
|
||||
- In the testData excel create a sheet with name of test. Eg. LoginTest
|
||||
- Create a execution sheet and make an entry of new test case. Eg. in the Regression sheet add a row for new test LoginTest and update other columns like run, mode etc.
|
||||
|
||||
##### 3. Execution
|
||||
To run test suite use below command.
|
||||
```sh
|
||||
npm run create:suite SHEET=<SheetName> && npm test
|
||||
```
|
||||
**Note:** SheetName needs to be updated.
|
||||
|
||||
To run individual test locally use below command.
|
||||
```sh
|
||||
set TEST_NAME=<TestFileName> && npm run local:test
|
||||
```
|
||||
**Note:** Using set command we are setting the local TestFileName.
|
||||
|
||||
To run individual test locally in [UI Mode](https://playwright.dev/docs/test-ui-mode) use below command.
|
||||
```sh
|
||||
set TEST_NAME=<TestFileName> && npm run local:test:ui
|
||||
```
|
||||
**Note:** Using set command we are setting the local TestFileName.
|
||||
|
||||
To change any environment configuration in .env file at run time use set command.
|
||||
Eg: To change browser to MS Edge use below command
|
||||
```sh
|
||||
set BROWSER=edge
|
||||
```
|
||||
Similar command can be used to update other environment configuration
|
||||
|
||||
To generate Allure report use below command
|
||||
```sh
|
||||
npm run report
|
||||
```
|
||||
|
||||
##### 4. Report & Logs
|
||||
Playwright HTML report will be present inside
|
||||
```sh
|
||||
test-results/results/index.html
|
||||
```
|
||||
Execution log will be present in the log file.
|
||||
```sh
|
||||
test-results/logs/execution.log
|
||||
```
|
||||
## ##
|
||||
**:pencil: If you find my work interesting don't forget to give a Star :star: & Follow me :busts_in_silhouette:**
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-minimal
|
||||
@@ -0,0 +1,86 @@
|
||||
# playwright-sample-project
|
||||
|
||||
## **Overview:**
|
||||
|
||||
This is a sample Playwright project using Typescript as scripting language and uses playwright-testrunner to execute test cases. This is a Data Driven framework focused on separating the test scripts logic and the test data from each other. This allows us to create test automation scripts by passing different sets of test data. The test data set is kept in an external Excel Sheet. The test scripts connect to the external Excel sheet to get the test data. This framework significantly reduces the number of test scripts compared to a modular based framework when we need to test for multiple sets of data for same functionality.
|
||||
|
||||
For Demo purpose UI test cases are created on [advantageonlineshopping.com](http://advantageonlineshopping.com/) site and API test cases are created on these [SOAP API](https://www.advantageonlineshopping.com/accountservice/ws/accountservice.wsdl) & [REST API](https://fakestoreapi.com) endpoints.
|
||||
|
||||
## Features
|
||||
|
||||
- This framework has built in library to operate on UI, API (both SOAP & REST API) and DB (MSSQL, DB2 & Oracle).
|
||||
- Supports execution of tests in different browsers.
|
||||
- Test data is stored in an Excel sheet and from this Excel sheet user can control the test cases that needs to be run.
|
||||
- User also has full control to run test in different modes from the Excel sheet.
|
||||
- Allows transfer of data between test cases.
|
||||
- Has utility built in for file download, Read PDF files etc.
|
||||
- Generates Playwright's HTML Report, Allure Report & JUnit Report in HTML format for each exaction.
|
||||
- Allure & Playwright report including snapshots and video in case of test failure.
|
||||
- Test execution logs are captured in the log file.
|
||||
- All the playwright related config is controlled by playwright config file.
|
||||
- Environment variables can be modified at runtime and its controlled by .env file.
|
||||
- Easy and simple integration to CI/CD tools like Jenkins.
|
||||
|
||||
#### Supported Browsers
|
||||
1. Chrome - default browser
|
||||
2. Firefox
|
||||
3. MS Edge
|
||||
4. WebKit - web browser engine used by Safari
|
||||
|
||||
#### Run Mode Details
|
||||
| Mode | Execl Value |Description |
|
||||
| ------ | ------ | ------ |
|
||||
|Normal|Blank| Runs the tests sequentially|
|
||||
|Serial|serial| Runs the tests sequentially. On test failure, all subsequent tests are skipped|
|
||||
|Parallel|parallel| Runs the tests parallelly, this is ideal when tests in the scenario are independent of one another|
|
||||
|
||||
#### Steps to use
|
||||
##### 1. Installation
|
||||
|
||||
Playwright framework requires [Node.js](https://nodejs.org/) v14+ to run.
|
||||
|
||||
Code from github need to be [download](https://github.com/VinayKumarBM/playwright-sample-project/archive/refs/heads/master.zip) OR [cloned](https://github.com/VinayKumarBM/playwright-sample-project.git) using git command.
|
||||
|
||||
Installing the dependencies.
|
||||
```sh
|
||||
npm ci
|
||||
```
|
||||
##### 2. Test creation
|
||||
- Create Test file with extenstion .spec.ts. Eg LoginTest.spec.ts
|
||||
- In the testData excel create a sheet with name of test. Eg. LoginTest
|
||||
- Create a execution sheet and make an entry of new test case. Eg. in the Regression sheet add a row for new test LoginTest and update other columns like run, mode etc.
|
||||
|
||||
##### 3. Execution
|
||||
To run test suite use below command.
|
||||
```sh
|
||||
npm run create:suite SHEET=<SheetName> && npm test
|
||||
```
|
||||
**Note:** SheetName needs to be updated.
|
||||
|
||||
To run individual test locally use below command.
|
||||
```sh
|
||||
set TEST_NAME=<TestFileName> && npm run local:test
|
||||
```
|
||||
**Note:** Using set command we are setting the local TestFileName.
|
||||
|
||||
To change any environment configuration in .env file at run time use set command.
|
||||
Eg: To change browser to MS Edge use below command
|
||||
```sh
|
||||
set BROWSER=edge
|
||||
```
|
||||
Similar command can be used to update other environment configuration
|
||||
|
||||
To generate Allure report use below command
|
||||
```sh
|
||||
npm run report
|
||||
```
|
||||
|
||||
##### 4. Report & Logs
|
||||
Playwright HTML report will be present inside
|
||||
```sh
|
||||
test-results/results/index.html
|
||||
```
|
||||
Execution log will be present in the log file.
|
||||
```sh
|
||||
test-results/logs/execution.log
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "playwright-demo",
|
||||
"version": "1.0.0",
|
||||
"description": "Playwright Demo framework",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": ""
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint . --ext .ts",
|
||||
"report": "allure serve",
|
||||
"create:suite": "cd ./src/framework/manager & npx ts-node SuiteManager.ts",
|
||||
"test": "playwright test --project=suite",
|
||||
"local:test": "playwright test --project=local",
|
||||
"local:test:ui": "playwright test --project=local --ui"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "YuCheng",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.54.1",
|
||||
"@types/easy-soap-request": "4.1.1",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/ibm_db": "2.0.10",
|
||||
"@types/mssql": "^9.1.7",
|
||||
"@types/oracledb": "5.2.3",
|
||||
"@types/pdf-parse": "^1.1.1",
|
||||
"@types/randomstring": "^1.1.8",
|
||||
"@types/string-format": "^2.0.0",
|
||||
"@types/xmldom": "^0.1.31",
|
||||
"@typescript-eslint/eslint-plugin": "^5.16.0",
|
||||
"@typescript-eslint/parser": "^5.16.0",
|
||||
"allure-playwright": "2.5.0",
|
||||
"dotenv": "17.2.1",
|
||||
"easy-soap-request": "^4.6.0",
|
||||
"eslint": "^8.12.0",
|
||||
"eslint-config-airbnb-typescript": "16.1.4",
|
||||
"eslint-plugin-playwright": "0.8.0",
|
||||
"ibm_db": "2.8.1",
|
||||
"jsonpath": "1.1.1",
|
||||
"moment": "2.29.4",
|
||||
"mssql": "^7.2.1",
|
||||
"oracledb": "5.3.0",
|
||||
"pdf-parse": "1.1.1",
|
||||
"randomstring": "1.2.2",
|
||||
"string-format": "2.0.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.5.4",
|
||||
"winston": "^3.4.0",
|
||||
"xml-formatter": "2.6.1",
|
||||
"xmldom": "0.6.0",
|
||||
"xpath": "0.0.32",
|
||||
"@types/convert-excel-to-json": "1.7.1",
|
||||
"convert-excel-to-json": "1.7.0",
|
||||
"jasmine-xml2html-converter": "0.0.2",
|
||||
"fetch-to-curl": "0.5.2",
|
||||
"monocart-reporter": "1.0.7"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,69 @@
|
||||
import { PlaywrightTestConfig } from "@playwright/test";
|
||||
import dotenv from 'dotenv';
|
||||
import Browser from "./src/framework/manager/Browser";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const timeInMin: number = 60 * 1000;
|
||||
const config: PlaywrightTestConfig = {
|
||||
use: {
|
||||
browserName: Browser.type(process.env.BROWSER.toLowerCase()),
|
||||
headless: false,
|
||||
channel: Browser.channel(process.env.BROWSER.toLowerCase()),
|
||||
launchOptions: {
|
||||
args: ["--start-maximized", "--disable-extensions", "--disable-plugins"],
|
||||
headless: false,
|
||||
timeout: Number.parseInt(process.env.BROWSER_LAUNCH_TIMEOUT, 10),
|
||||
slowMo: 100,
|
||||
downloadsPath: "./test-results/downloads",
|
||||
},
|
||||
viewport: null,
|
||||
ignoreHTTPSErrors: true,
|
||||
acceptDownloads: true,
|
||||
actionTimeout: Number.parseInt(process.env.ACTION_TIMEOUT, 10) * timeInMin,
|
||||
navigationTimeout: Number.parseInt(process.env.NAVIGATION_TIMEOUT, 10) * timeInMin,
|
||||
screenshot: {
|
||||
mode: "only-on-failure",
|
||||
fullPage: true,
|
||||
},
|
||||
video: "retain-on-failure",
|
||||
},
|
||||
testDir: "./src/tests",
|
||||
outputDir: "./test-results/failure",
|
||||
retries: Number.parseInt(process.env.RETRIES, 10),
|
||||
preserveOutput: "failures-only",
|
||||
reportSlowTests: null,
|
||||
timeout: Number.parseInt(process.env.TEST_TIMEOUT, 10) * timeInMin,
|
||||
workers: Number.parseInt(process.env.PARALLEL_THREAD, 10),
|
||||
reporter: [
|
||||
["dot"],
|
||||
["allure-playwright", {
|
||||
detail: false,
|
||||
suiteTitle: false,
|
||||
environmentInfo: {
|
||||
OS: process.platform.toUpperCase(),
|
||||
BROWSER: process.env.BROWSER.toUpperCase(),
|
||||
BASE_URL: process.env.BASE_URL,
|
||||
},
|
||||
}],
|
||||
['html', { open: 'never', outputFolder: "./test-results/report" }],
|
||||
["junit", { outputFile: "./test-results/results/results.xml" }],
|
||||
["json", { outputFile: "./test-results/results/results.json" }],
|
||||
["./src/framework/logger/TestListener.ts"],
|
||||
['monocart-reporter', {
|
||||
name: "Automation Report",
|
||||
outputFile: './test-results/report/execution.html',
|
||||
}],
|
||||
],
|
||||
projects: [
|
||||
{
|
||||
name: "local",
|
||||
testMatch: `*${process.env.TEST_NAME.trim()}*`,
|
||||
},
|
||||
{
|
||||
name: "suite",
|
||||
testMatch: "*.test.ts",
|
||||
},
|
||||
],
|
||||
};
|
||||
export default config;
|
||||
@@ -0,0 +1,6 @@
|
||||
export default class RESTConstants {
|
||||
static readonly CONTENT_TYPE = 'Content-Type';
|
||||
static readonly ACCEPT = 'Accept';
|
||||
static readonly CONTENT_JSON = "application/json";
|
||||
static readonly STATUS_CODE = "Status Code";
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import test, { Page } from "@playwright/test";
|
||||
import APIActions from "@apiActions/APIActions";
|
||||
import RESTResponse from "@apiActions/RESTResponse";
|
||||
import Assert from "@asserts/Assert";
|
||||
import RESTConstants from "@restConstants/RESTConstants";
|
||||
|
||||
export default class UserSteps {
|
||||
private api: APIActions;
|
||||
private BASE_URL = process.env.REST_API_BASE_URL;
|
||||
constructor(private page: Page) {
|
||||
this.api = new APIActions(this.page);
|
||||
}
|
||||
private get header() {
|
||||
return this.api.header.set(RESTConstants.CONTENT_TYPE, RESTConstants.CONTENT_JSON)
|
||||
.set(RESTConstants.ACCEPT, RESTConstants.CONTENT_JSON).get();
|
||||
}
|
||||
|
||||
public async get(endPoint: string, operation: string): Promise<RESTResponse> {
|
||||
let response: RESTResponse;
|
||||
await test.step(`Making call to GET ${operation}`, async () => {
|
||||
response = await this.api.rest.get(this.BASE_URL + endPoint, this.header, operation);
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
public async post(endPoint: string, requestBodyFile: string, requestData: any,
|
||||
operation: string): Promise<RESTResponse> {
|
||||
let response: RESTResponse;
|
||||
await test.step(`Making POST call to ${operation}`, async () => {
|
||||
const requestJSON = await this.api.rest.createRequestBody(requestBodyFile, requestData);
|
||||
response = await this.api.rest.post(this.BASE_URL + endPoint, this.header, requestJSON, operation);
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
public async put(endPoint: string, requestBodyFile: string, requestData: any,
|
||||
operation: string): Promise<RESTResponse> {
|
||||
let response: RESTResponse;
|
||||
await test.step(`Making PUT call to ${operation}`, async () => {
|
||||
const requestJSON = await this.api.rest.createRequestBody(requestBodyFile, requestData);
|
||||
response = await this.api.rest.put(this.BASE_URL + endPoint, this.header, requestJSON, operation);
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
public async delete(endPoint: string, operation: string): Promise<RESTResponse> {
|
||||
let response: RESTResponse;
|
||||
await test.step(`Making DELETE call to ${operation}`, async () => {
|
||||
response = await this.api.rest.delete(this.BASE_URL + endPoint, this.header, operation);
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
public async verifyStatusCode(response: RESTResponse, statusCode: string) {
|
||||
await test.step(`Verifying that status code is ${statusCode}`, async () => {
|
||||
await Assert.assertEquals(await response.getStatusCode(), statusCode, RESTConstants.STATUS_CODE);
|
||||
});
|
||||
}
|
||||
|
||||
public async extractResponseValue(response: RESTResponse, jsonPath: string, operation: string) {
|
||||
let value: string;
|
||||
await test.step(`Extract value from ${operation} response`, async () => {
|
||||
value = await response.getTagContentByJsonPath(jsonPath, operation);
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
public async verifyContent(response: RESTResponse, jsonPath: string, expectedValue: string, description: string) {
|
||||
await test.step(`Verifying that ${description} has value ${expectedValue}`, async () => {
|
||||
const value = await response.getTagContentByJsonPath(jsonPath, description);
|
||||
await Assert.assertEquals(value, expectedValue, description);
|
||||
});
|
||||
}
|
||||
|
||||
public async verifyContentIsNotNull(response: RESTResponse, jsonPath: string, description: string) {
|
||||
await test.step(`Verifying that ${description} content is NOT NULL`, async () => {
|
||||
const value = await response.getTagContentByJsonPath(jsonPath, description);
|
||||
await Assert.assertNotNull(value, description);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export default class SOAPConstants {
|
||||
static readonly CONTENT_TYPE = 'Content-Type';
|
||||
static readonly SOAP_ACTION = 'SoapAction';
|
||||
static readonly CONTENT_TEXT = "text/xml;charset=UTF-8";
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import test, { Page } from "@playwright/test";
|
||||
import APIActions from "@apiActions/APIActions";
|
||||
import SOAPResponse from "@apiActions/SOAPResponse";
|
||||
import Assert from "@asserts/Assert";
|
||||
import SOAPConstants from "@soapConstants/SOAPConstants";
|
||||
|
||||
export default class AccountServiceSteps {
|
||||
private api: APIActions;
|
||||
|
||||
constructor(private page: Page) {
|
||||
this.api = new APIActions(this.page);
|
||||
}
|
||||
|
||||
public async request(endPoint: string, requestBody: string, requestData: any,
|
||||
operation: string): Promise<SOAPResponse> {
|
||||
let response: SOAPResponse;
|
||||
await test.step(`SOAP request to ${operation}`, async () => {
|
||||
const requestHeaders = this.api.header.set(SOAPConstants.CONTENT_TYPE, SOAPConstants.CONTENT_TEXT).get();
|
||||
response = await this.api.soap.post(endPoint, requestHeaders, requestBody, requestData, operation);
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
public async verifyResponse(response: SOAPResponse, xpath: string, result: string, operation: string) {
|
||||
await test.step(`Verifying that result of ${operation} is ${result}`, async () => {
|
||||
const actualResult = await response.getTagContentByXpath(xpath, operation);
|
||||
await Assert.assertEquals(actualResult, result.toString(), operation);
|
||||
});
|
||||
}
|
||||
|
||||
public async verifyResponseContains(response: SOAPResponse, xpath: string, result: string, operation: string) {
|
||||
await test.step(`Verifying that result of ${operation} contains ${result}`, async () => {
|
||||
const actualResult = await response.getTagContentByXpath(xpath, operation);
|
||||
await Assert.assertContains(actualResult, result, operation);
|
||||
});
|
||||
}
|
||||
|
||||
public async getResponseContent(response: SOAPResponse, xpath: string, operation: string) {
|
||||
let content: string;
|
||||
await test.step(`Getting content from response of ${operation}`, async () => {
|
||||
content = await response.getTagContentByXpath(xpath, operation);
|
||||
});
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default class CommonConstants {
|
||||
static readonly FALSE = 'false';
|
||||
static readonly TRUE = 'true';
|
||||
static readonly TEN = 10;
|
||||
static readonly TWO = 2;
|
||||
static readonly DOWNLOADS_PATH = "./test-results/downloads/";
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default class ConfigurationConstants {
|
||||
static readonly AOS_BACKEND = "AOS Backend";
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
export default class HomePageConstants {
|
||||
static readonly USER_ICON = "User Icon";
|
||||
static readonly USER_NAME = "User Name";
|
||||
static readonly PASSWORD = "Password";
|
||||
static readonly REMEMBER_ME_CHECKBOX = "Remember Me";
|
||||
static readonly SIGN_IN_BUTTON = "Sign In Button";
|
||||
static readonly SIGN_IN_ERROR_MESSAGE = "Error Message";
|
||||
static readonly SIGN_OUT_LINK = "Sign Out Link";
|
||||
static readonly HOME_PAGE = "Home Page";
|
||||
static readonly CREATE_NEW_ACCOUNT_LINK = "Create New Account Link";
|
||||
static readonly CATEGORY_DROPDOWN = "Category Dropdown";
|
||||
static readonly PRODUCT_DROPDOWN = "Product Dropdown";
|
||||
static readonly SUBJECT_TEXTAREA = "ContactUs Subject";
|
||||
static readonly EMAIL_TEXTBOX = "ContactUs email";
|
||||
static readonly SEND_BUTTON = "Send Button";
|
||||
static readonly CONTACT_US_MESSAGE = "ContactUs Success Message";
|
||||
static readonly SEARCH_ICON = "Search Icon";
|
||||
static readonly SEARCH_TEXTBOX = "Search Box";
|
||||
static readonly SEARCH_CLOSE_IMAGE = "Close Search";
|
||||
static readonly ENTER_KEY: 'Enter';
|
||||
static readonly HELP_ICON = "Help Icon";
|
||||
static readonly MANAGEMENT_CONSOLE_LINK = "Management Console Link";
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export default class RegistrationPageConstants {
|
||||
static readonly USER_NAME = "User Name";
|
||||
static readonly PASSWORD = "Password";
|
||||
static readonly EMAIL = "Email";
|
||||
static readonly CONFIRM_PASSWORD = "Confirm Password";
|
||||
static readonly FIRST_NAME = "First Name";
|
||||
static readonly LAST_NAME = "Last Name";
|
||||
static readonly PHONE_NUMBER = "Phone Number";
|
||||
static readonly COUNTRY = "Country";
|
||||
static readonly CITY = "City";
|
||||
static readonly ADDRESS = "Address";
|
||||
static readonly STATE = "State";
|
||||
static readonly POSTAL_CODE = "Postal Code";
|
||||
static readonly PROMOTION = "Promotion";
|
||||
static readonly TERMS_AND_CONDITIONS = "Terms & Conditions";
|
||||
static readonly REGISTER_BUTTON = "Register Button";
|
||||
static readonly ALREADY_HAVE_AN_ACCOUNT_LINK = "Already have an Account Link";
|
||||
static readonly ERROR_MESSAGE = "Error Message";
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default class ConfigurationPage {
|
||||
static readonly AOS_BACK_END_LINK = "//ul[@class='nav_user_guide_list']/li/a[text()='AOS back end']";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
export default class HomePage {
|
||||
static readonly USER_ICON = "#menuUser";
|
||||
static readonly USER_NAME_TEXTBOX = "[name='username']";
|
||||
static readonly PASSWORD_TEXTBOX = "[name='password']";
|
||||
static readonly REMEMBER_ME_CHECKBOX = "[name='remember_me']";
|
||||
static readonly SIGN_IN_BUTTON = "#sign_in_btn";
|
||||
static readonly LOGGED_IN_USER = "a#menuUserLink>span.hi-user";
|
||||
static readonly SIGN_IN_ERROR_MESSAGE = "#signInResultMessage.invalid";
|
||||
static readonly SIGN_OUT_LINK = "#loginMiniTitle>[translate='Sign_out']";
|
||||
static readonly CREATE_NEW_ACCOUNT_LINK = "[translate='CREATE_NEW_ACCOUNT']";
|
||||
static readonly CATEGORY_DROPDOWN = "[name='categoryListboxContactUs']";
|
||||
static readonly PRODUCT_DROPDOWN = "[name='productListboxContactUs']";
|
||||
static readonly SUBJECT_TEXTAREA = "[name='subjectTextareaContactUs']";
|
||||
static readonly EMAIL_TEXTBOX = "[name='emailContactUs']";
|
||||
static readonly SEND_BUTTON = "#send_btn";
|
||||
static readonly CONTACT_US_MESSAGE = ".roboto-regular.successMessage";
|
||||
static readonly SEARCH_ICON = "#searchSection";
|
||||
static readonly SEARCH_TEXTBOX = "#autoComplete";
|
||||
static readonly SEARCH_CLOSE_IMAGE = "div.autoCompleteCover>div>img";
|
||||
static readonly HELP_ICON = "#menuHelp";
|
||||
static readonly MANAGEMENT_CONSOLE_LINK = "div#helpMiniTitle [translate='CONFIG_TOOL']";
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export default class RegistrationPage {
|
||||
static readonly USER_NAME_TEXTBOX = "[name='usernameRegisterPage']";
|
||||
static readonly PASSWORD_TEXTBOX = "[name='passwordRegisterPage']";
|
||||
static readonly EMAIL_TEXTBOX = "[name='emailRegisterPage']";
|
||||
static readonly PASSWORD_CONFIRM_TEXTBOX = "[name='confirm_passwordRegisterPage']";
|
||||
static readonly FIRST_NAME_TEXTBOX = "[name='first_nameRegisterPage']";
|
||||
static readonly LAST_NAME_TEXTBOX = "[name='last_nameRegisterPage']";
|
||||
static readonly PHONE_NUMBER_TEXTBOX = "[name='phone_numberRegisterPage']";
|
||||
static readonly COUNTRY_DROPDOWN = "[name='countryListboxRegisterPage']";
|
||||
static readonly CITY_TEXTBOX = "[name='cityRegisterPage']";
|
||||
static readonly ADDRESS_TEXTBOX = "[name='addressRegisterPage']";
|
||||
static readonly STATE_TEXTBOX = "[name='state_/_province_/_regionRegisterPage']";
|
||||
static readonly POSTAL_CODE_TEXTBOX = "[name='postal_codeRegisterPage']";
|
||||
static readonly PROMOTION_CHECKBOX = "[name='allowOffersPromotion']";
|
||||
static readonly PRIVACY_POLICY_CHECKBOX = "[name='i_agree']";
|
||||
static readonly REGISTER_BUTTON = "#register_btn";
|
||||
static readonly ALREADY_HAVE_AN_ACCOUNT_LINK = "[translate='ALREADY_HAVE_AN_ACCOUNT']";
|
||||
static readonly MANDATORY_FIELD_ERROR_MESSAGE = "div.inputContainer>label.invalid";
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import test, { Page } from "@playwright/test";
|
||||
import ConfigurationPage from "@pages/ConfigurationPage";
|
||||
import Assert from "@asserts/Assert";
|
||||
import UIActions from "@uiActions/UIActions";
|
||||
import PDFUtil from "@utils/PDFUtil";
|
||||
import CommonConstants from "@uiConstants/CommonConstants";
|
||||
import ConfigurationConstants from "@uiConstants/ConfigurationConstants";
|
||||
|
||||
export default class ConfigurationSteps {
|
||||
private ui: UIActions;
|
||||
|
||||
constructor(private page: Page) {
|
||||
this.ui = new UIActions(page);
|
||||
}
|
||||
|
||||
public async downloadAOSBackendPDF() {
|
||||
let fileName: string;
|
||||
await test.step(`Downloading the AOS backend PDF file`, async () => {
|
||||
fileName = await this.ui.downloadFile(ConfigurationPage.AOS_BACK_END_LINK,
|
||||
ConfigurationConstants.AOS_BACKEND);
|
||||
});
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public async verifyPDFFilePageCount(fileName: string, pages: number) {
|
||||
await test.step(`Verify that ${fileName} file has ${pages} pages`, async () => {
|
||||
await Assert.assertEquals(await PDFUtil.getNumberOfPages(CommonConstants.DOWNLOADS_PATH + fileName),
|
||||
pages, fileName);
|
||||
});
|
||||
}
|
||||
|
||||
public async verifyPDFFileText(fileName: string, content: string) {
|
||||
await test.step(`Verify that ${fileName} has content ${content}`, async () => {
|
||||
await Assert.assertContains(await PDFUtil.getText(CommonConstants.DOWNLOADS_PATH + fileName),
|
||||
content, fileName);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import test, { Page } from "@playwright/test";
|
||||
import UIActions from "@uiActions/UIActions";
|
||||
import Assert from "@asserts/Assert";
|
||||
import CommonConstants from "@uiConstants/CommonConstants";
|
||||
import HomePageConstants from "@uiConstants/HomePageConstants";
|
||||
import HomePage from "@pages/HomePage";
|
||||
|
||||
export default class HomeSteps {
|
||||
private ui: UIActions;
|
||||
|
||||
constructor(private page: Page) {
|
||||
this.ui = new UIActions(page);
|
||||
}
|
||||
/**
|
||||
* Launch the Application
|
||||
*/
|
||||
public async launchApplication() {
|
||||
await test.step(`Launching the application`, async () => {
|
||||
await this.ui.goto(process.env.BASE_URL, HomePageConstants.HOME_PAGE);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Log into the application
|
||||
* @param userName
|
||||
* @param password
|
||||
*/
|
||||
public async login(userName: string, password: string) {
|
||||
await test.step(`Login to application credentials as ${userName} & ${password}`, async () => {
|
||||
await this.ui.element(HomePage.USER_ICON, HomePageConstants.USER_ICON).click();
|
||||
await this.enterLoginDetails(userName, password);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Enter login details
|
||||
* @param userName
|
||||
* @param password
|
||||
*/
|
||||
public async enterLoginDetails(userName: string, password: string) {
|
||||
await test.step(`Enter login credentials as ${userName} & ${password}`, async () => {
|
||||
await this.ui.editBox(HomePage.USER_NAME_TEXTBOX, HomePageConstants.USER_NAME).fill(userName);
|
||||
await this.ui.editBox(HomePage.PASSWORD_TEXTBOX, HomePageConstants.PASSWORD).fill(password);
|
||||
await this.ui.checkbox(HomePage.REMEMBER_ME_CHECKBOX, HomePageConstants.REMEMBER_ME_CHECKBOX).check();
|
||||
await this.ui.element(HomePage.SIGN_IN_BUTTON, HomePageConstants.SIGN_IN_BUTTON).click();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Validate logged in user
|
||||
* @param userName
|
||||
*/
|
||||
public async validateLogin(userName: string) {
|
||||
await test.step(`Verify that user is successfully logged in as ${userName}`, async () => {
|
||||
const user = await this.ui.element(HomePage.LOGGED_IN_USER, HomePageConstants.USER_NAME).getTextContent();
|
||||
await Assert.assertEquals(user, userName, HomePageConstants.USER_NAME);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Validate invalid login
|
||||
* @param errorMessage
|
||||
*/
|
||||
public async validateInvalidLogin(errorMessage: string) {
|
||||
await test.step(`Verify that error message ${errorMessage}`, async () => {
|
||||
const user = await this.ui.element(HomePage.SIGN_IN_ERROR_MESSAGE, HomePageConstants.SIGN_IN_ERROR_MESSAGE)
|
||||
.getTextContent();
|
||||
await Assert.assertEquals(user, errorMessage, HomePageConstants.SIGN_IN_ERROR_MESSAGE);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Log out of the application
|
||||
*/
|
||||
public async logout() {
|
||||
await test.step(`Logged out of application`, async () => {
|
||||
await this.ui.element(HomePage.LOGGED_IN_USER, HomePageConstants.USER_NAME).click();
|
||||
await this.ui.element(HomePage.SIGN_OUT_LINK, HomePageConstants.SIGN_OUT_LINK).click();
|
||||
await this.ui.pauseInSecs(CommonConstants.TWO);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Navigate to Create Account page
|
||||
*/
|
||||
public async navigateToCreateAccount() {
|
||||
await test.step(`Navigate to Create Account page`, async () => {
|
||||
await this.ui.element(HomePage.USER_ICON, HomePageConstants.USER_ICON).click();
|
||||
await this.ui.element(HomePage.CREATE_NEW_ACCOUNT_LINK, HomePageConstants.CREATE_NEW_ACCOUNT_LINK).click();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Enters details into Contact Us
|
||||
* @param category
|
||||
* @param product
|
||||
* @param email
|
||||
* @param subject
|
||||
*/
|
||||
public async enterContactUsDetails(category: string, product: string, email: string, subject: string) {
|
||||
await test.step(`Entering Contact Us details`, async () => {
|
||||
await this.ui.dropdown(HomePage.CATEGORY_DROPDOWN, HomePageConstants.CATEGORY_DROPDOWN)
|
||||
.selectByVisibleText(category);
|
||||
await this.ui.dropdown(HomePage.PRODUCT_DROPDOWN, HomePageConstants.PRODUCT_DROPDOWN)
|
||||
.selectByVisibleText(product);
|
||||
await this.ui.editBox(HomePage.EMAIL_TEXTBOX, HomePageConstants.EMAIL_TEXTBOX).fill(email);
|
||||
await this.ui.editBox(HomePage.SUBJECT_TEXTAREA, HomePageConstants.SUBJECT_TEXTAREA).fill(subject);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Click on Send button of Contact Us
|
||||
*/
|
||||
public async sendMessage() {
|
||||
await test.step(`Click on Send button of Contact Us`, async () => {
|
||||
await this.ui.element(HomePage.SEND_BUTTON, HomePageConstants.SEND_BUTTON).click();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Verify the success message of Contact Us
|
||||
* @param message
|
||||
*/
|
||||
public async verifySuccessMessage(message: string) {
|
||||
await test.step(`Verifying Success Message of Contact Us`, async () => {
|
||||
const actualMessage = await this.ui.element(HomePage.CONTACT_US_MESSAGE,
|
||||
HomePageConstants.CONTACT_US_MESSAGE).getTextContent();
|
||||
await Assert.assertEquals(actualMessage, message, HomePageConstants.CONTACT_US_MESSAGE);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Search for Product
|
||||
* @param product
|
||||
*/
|
||||
public async searchProduct(product: string) {
|
||||
await test.step(`Searching for product '${product}'`, async () => {
|
||||
await this.ui.element(HomePage.SEARCH_ICON, HomePageConstants.SEARCH_ICON).click();
|
||||
await (await this.ui.editBox(HomePage.SEARCH_TEXTBOX, HomePageConstants.SEARCH_TEXTBOX).type(product))
|
||||
.keyPress(HomePageConstants.ENTER_KEY);
|
||||
await this.ui.element(HomePage.SEARCH_CLOSE_IMAGE, HomePageConstants.SEARCH_CLOSE_IMAGE).click();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Navigate to Management Console screen
|
||||
*/
|
||||
public async navigateToManagementConsole() {
|
||||
let newPage: Page;
|
||||
await test.step(`Navigate to Management Console screen`, async () => {
|
||||
await this.ui.waitForLoadingImage();
|
||||
await this.ui.element(HomePage.HELP_ICON, HomePageConstants.HELP_ICON).click();
|
||||
newPage = await this.ui.switchToNewWindow(HomePage.MANAGEMENT_CONSOLE_LINK,
|
||||
HomePageConstants.MANAGEMENT_CONSOLE_LINK);
|
||||
});
|
||||
return newPage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import test, { Page } from "@playwright/test";
|
||||
import UIActions from "@uiActions/UIActions";
|
||||
import Assert from "@asserts/Assert";
|
||||
import StringUtil from "@utils/StringUtil";
|
||||
import CommonConstants from "@uiConstants/CommonConstants";
|
||||
import RegistrationPageConstants from "@uiConstants/RegistrationPageConstants";
|
||||
import RegistrationPage from "@pages/RegistrationPage";
|
||||
|
||||
export default class RegistrationSteps {
|
||||
private ui: UIActions;
|
||||
|
||||
constructor(private page: Page) {
|
||||
this.ui = new UIActions(page);
|
||||
}
|
||||
/**
|
||||
* Creating a new Account
|
||||
* @param email
|
||||
* @param password
|
||||
* @param confirmPassword
|
||||
* @param firstName
|
||||
* @param lastName
|
||||
* @param phoneNumber
|
||||
* @param country
|
||||
* @param city
|
||||
* @param address
|
||||
* @param state
|
||||
* @param postalCode
|
||||
* @param allowOffersPromotion
|
||||
* @returns
|
||||
*/
|
||||
public async createAccount(email: string, password: string, confirmPassword: string, firstName: string,
|
||||
lastName: string, phoneNumber: string, country: string, city: string, address: string, state: string,
|
||||
postalCode: string, allowOffersPromotion: string) {
|
||||
let userName: string;
|
||||
await test.step(`Create New Account`, async () => {
|
||||
userName = StringUtil.randomAlphabeticString(CommonConstants.TEN);
|
||||
await this.ui.editBox(RegistrationPage.USER_NAME_TEXTBOX,
|
||||
RegistrationPageConstants.USER_NAME).fill(userName);
|
||||
await this.ui.editBox(RegistrationPage.EMAIL_TEXTBOX, RegistrationPageConstants.EMAIL).fill(email);
|
||||
await this.ui.editBox(RegistrationPage.PASSWORD_TEXTBOX, RegistrationPageConstants.PASSWORD).fill(password);
|
||||
await this.ui.editBox(RegistrationPage.PASSWORD_CONFIRM_TEXTBOX, RegistrationPageConstants.CONFIRM_PASSWORD)
|
||||
.fill(confirmPassword);
|
||||
await this.ui.editBox(RegistrationPage.FIRST_NAME_TEXTBOX, RegistrationPageConstants.FIRST_NAME)
|
||||
.fill(firstName);
|
||||
await this.ui.editBox(RegistrationPage.LAST_NAME_TEXTBOX, RegistrationPageConstants.LAST_NAME)
|
||||
.fill(lastName);
|
||||
await this.ui.editBox(RegistrationPage.PHONE_NUMBER_TEXTBOX, RegistrationPageConstants.PHONE_NUMBER)
|
||||
.fill(phoneNumber);
|
||||
await this.ui.dropdown(RegistrationPage.COUNTRY_DROPDOWN, RegistrationPageConstants.COUNTRY)
|
||||
.selectByVisibleText(country);
|
||||
await this.ui.editBox(RegistrationPage.CITY_TEXTBOX, RegistrationPageConstants.CITY).fill(city);
|
||||
await this.ui.editBox(RegistrationPage.ADDRESS_TEXTBOX, RegistrationPageConstants.ADDRESS).fill(address);
|
||||
await this.ui.editBox(RegistrationPage.STATE_TEXTBOX, RegistrationPageConstants.STATE).fill(state);
|
||||
await this.ui.editBox(RegistrationPage.POSTAL_CODE_TEXTBOX, RegistrationPageConstants.POSTAL_CODE)
|
||||
.fill(postalCode);
|
||||
await Assert.assertTrue(await this.ui.checkbox(RegistrationPage.PROMOTION_CHECKBOX,
|
||||
RegistrationPageConstants.PROMOTION).isChecked(), RegistrationPageConstants.PROMOTION);
|
||||
if (allowOffersPromotion.toLowerCase() === CommonConstants.FALSE) {
|
||||
await this.ui.checkbox(RegistrationPage.PROMOTION_CHECKBOX, RegistrationPageConstants.PROMOTION)
|
||||
.uncheck();
|
||||
}
|
||||
await this.ui.checkbox(RegistrationPage.PRIVACY_POLICY_CHECKBOX,
|
||||
RegistrationPageConstants.TERMS_AND_CONDITIONS).check();
|
||||
});
|
||||
return userName;
|
||||
}
|
||||
/**
|
||||
* Saves the registration details
|
||||
*/
|
||||
public async saveRegistration() {
|
||||
await test.step(`Save registration details`, async () => {
|
||||
await this.ui.element(RegistrationPage.REGISTER_BUTTON, RegistrationPageConstants.REGISTER_BUTTON).click();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Click on link Already having Account
|
||||
*/
|
||||
public async alreadyHaveAccount() {
|
||||
await test.step(`Click on Already have an account link`, async () => {
|
||||
await this.ui.element(RegistrationPage.ALREADY_HAVE_AN_ACCOUNT_LINK,
|
||||
RegistrationPageConstants.ALREADY_HAVE_AN_ACCOUNT_LINK).click();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default class DatabaseConstants {
|
||||
static readonly QUERY_EXECUTION = "Query Execution";
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import test from "@playwright/test";
|
||||
import { IRecordSet } from "mssql";
|
||||
import Assert from "@asserts/Assert";
|
||||
import DBUtil from "@utils/DBUtil";
|
||||
import DatabaseConstants from "@dbConstants/DatabaseConstants";
|
||||
|
||||
export default class DatabaseStep {
|
||||
public async executeMSSQLQuery(query: string) {
|
||||
let result: { rows: IRecordSet<any>; rowsAffected: number[]; };
|
||||
await test.step('Executing query in MS SQL db', async () => {
|
||||
result = await DBUtil.executeMSSQLQuery(process.env.DB_CONFIG, query);
|
||||
console.log(result);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public async executeDB2Query(query: string) {
|
||||
let result: { rows: any; rowsAffected: any; };
|
||||
await test.step('Executing query in DB2 db', async () => {
|
||||
result = await DBUtil.executeDB2Query(process.env.DB_CONFIG, query);
|
||||
console.log(result);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public async executeOracleQuery(query: string) {
|
||||
let result: { rows: unknown[]; rowsAffected: number; };
|
||||
await test.step('Executing query in Oracle db', async () => {
|
||||
result = await DBUtil.executeOracleQuery(process.env.DB_CONFIG, query);
|
||||
console.log(result);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public async verifyExecutionSuccess(rowsAffected: number) {
|
||||
await test.step('Verify query execution is success', async () => {
|
||||
await Assert.assertTrue(rowsAffected > 0, DatabaseConstants.QUERY_EXECUTION);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { test as base } from '@playwright/test';
|
||||
|
||||
export const test = base.extend<{ MyFixtures }, { gData: Map<string, any> }>({
|
||||
gData: [async ({ }, use) => {
|
||||
const data = new Map<string, any>();
|
||||
data.set("SPACE", " ");
|
||||
data.set("HYPHEN", "-");
|
||||
data.set("UNDERSCORE", "_");
|
||||
await use(data);
|
||||
}, { scope: 'worker' }],
|
||||
|
||||
page: async ({ page, gData }, use) => {
|
||||
await use(page);
|
||||
},
|
||||
});
|
||||
export { expect } from '@playwright/test';
|
||||
@@ -0,0 +1,9 @@
|
||||
export default class BrowserConstants {
|
||||
static readonly CHROME = "chrome";
|
||||
static readonly FIREFOX = "firefox";
|
||||
static readonly WEBKIT = "webkit";
|
||||
static readonly MSEDGE = "msedge";
|
||||
static readonly EDGE = "edge";
|
||||
static readonly CHROMIUM = "chromium";
|
||||
static readonly BLANK = "";
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
export default class CommonConstants {
|
||||
static readonly SEMICOLON = ';';
|
||||
static readonly BLANK = '';
|
||||
static readonly ZERO = 0;
|
||||
static readonly ONE = 1;
|
||||
static readonly TWO = 2;
|
||||
static readonly THREE = 3;
|
||||
static readonly HALF = 0.5;
|
||||
static readonly ONE_THOUSAND = 1000;
|
||||
static readonly DOWNLOAD_PATH = "./test-results/downloads/";
|
||||
static readonly SOAP_XML_REQUEST_PATH = "src/resources/API/SOAP/";
|
||||
static readonly REST_JSON_REQUEST_PATH = "src/resources/API/REST/";
|
||||
static readonly TEST_FOLDER_PATH = "../../tests/";
|
||||
static readonly TEST_SUITE_FILE_FORMAT = ".test.ts";
|
||||
static readonly PARALLEL_MODE = "parallel";
|
||||
static readonly SERIAL_MODE = "serial";
|
||||
static readonly REPORT_TITLE = "Test Execution Report";
|
||||
static readonly RESULTS_PATH = "./test-results/results";
|
||||
static readonly JUNIT_RESULTS_PATH = `${CommonConstants.RESULTS_PATH}/results.xml`;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default class DBConstants {
|
||||
static readonly PROTOCOL = ';PROTOCOL=TCPIP';
|
||||
static readonly CERTIFICATE = ';trustServerCertificate=true;encrypt=false';
|
||||
static readonly USER = 'user:';
|
||||
static readonly PASSWORD = 'password:';
|
||||
static readonly CONNECTION_STRING = 'connectString:';
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export default class ExcelConstants {
|
||||
static readonly TEST_PATH = './src/resources/data/testData.xlsx';
|
||||
static readonly SUITE_PATH = '../../resources/data/testData.xlsx';
|
||||
static readonly YES = "YES";
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export default class HTMLConstants {
|
||||
static readonly LOADING_IMAGE = "body>.loader";
|
||||
static readonly OPTION = "option";
|
||||
static readonly SELECTED_OPTION = "option[selected='selected']";
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import winston from 'winston';
|
||||
|
||||
const Logger = winston.createLogger({
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.uncolorize({ level: true, message: true, raw: true }),
|
||||
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
winston.format.align(),
|
||||
winston.format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`),
|
||||
),
|
||||
}),
|
||||
new winston.transports.File({
|
||||
filename: 'test-results/logs/execution.log',
|
||||
format: winston.format.combine(
|
||||
winston.format.uncolorize({ level: true, message: true, raw: true }),
|
||||
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
winston.format.align(),
|
||||
winston.format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`),
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
export default Logger;
|
||||
@@ -0,0 +1,57 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import {
|
||||
Reporter, TestCase, TestError, TestResult, TestStep,
|
||||
} from "@playwright/test/reporter";
|
||||
import Logger from "./Logger";
|
||||
|
||||
const TEST_SEPARATOR = "##############################################################################";
|
||||
const STEP_SEPARATOR = "------------------------------------------------------------------------------";
|
||||
|
||||
export default class TestListener implements Reporter {
|
||||
onTestBegin(test: TestCase, result: TestResult): void {
|
||||
this.printLogs(`Test: ${test.title} - Started`, TEST_SEPARATOR);
|
||||
}
|
||||
|
||||
onTestEnd(test: TestCase, result: TestResult): void {
|
||||
if (result.status === 'failed') {
|
||||
Logger.error(`Test: ${test.title} - ${result.status}\n${result.error.stack}`);
|
||||
}
|
||||
this.printLogs(`Test: ${test.title} - ${result.status}`, TEST_SEPARATOR);
|
||||
}
|
||||
|
||||
onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult): void {
|
||||
Logger.info(chunk);
|
||||
}
|
||||
|
||||
onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult): void {
|
||||
Logger.error(chunk);
|
||||
}
|
||||
|
||||
onStepBegin(test: TestCase, result: TestResult, step: TestStep): void {
|
||||
if (step.category === "test.step") {
|
||||
if (typeof step.parent !== "undefined") {
|
||||
Logger.info(step.title);
|
||||
} else {
|
||||
this.printLogs(`Started Step: ${step.title}`, STEP_SEPARATOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onStepEnd(test: TestCase, result: TestResult, step: TestStep): void {
|
||||
if (step.category === "test.step" && typeof step.parent === "undefined") {
|
||||
this.printLogs(`Completed Step: ${step.title}`, STEP_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
onError(error: TestError): void {
|
||||
Logger.error(`Message: ${error.message}`);
|
||||
Logger.error(`Stack: ${error.stack}`);
|
||||
Logger.error(`Value: ${error.value}`);
|
||||
}
|
||||
|
||||
private printLogs(msg: string, separator: string) {
|
||||
Logger.info(separator);
|
||||
Logger.info(`${msg.toUpperCase()}`);
|
||||
Logger.info(separator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import BrowserConstants from "../constants/BrowserConstants";
|
||||
|
||||
export default class Browser {
|
||||
public static type(browser: string) {
|
||||
let browserType;
|
||||
if (browser === BrowserConstants.FIREFOX) {
|
||||
browserType = BrowserConstants.FIREFOX;
|
||||
} else if (browser === BrowserConstants.WEBKIT) {
|
||||
browserType = BrowserConstants.WEBKIT;
|
||||
} else {
|
||||
browserType = BrowserConstants.CHROMIUM;
|
||||
}
|
||||
return browserType;
|
||||
}
|
||||
|
||||
public static channel(browser: string) {
|
||||
let browserChannel;
|
||||
if (browser === BrowserConstants.CHROME) {
|
||||
browserChannel = BrowserConstants.CHROME;
|
||||
} else if (browser === BrowserConstants.EDGE) {
|
||||
browserChannel = BrowserConstants.MSEDGE;
|
||||
} else {
|
||||
browserChannel = BrowserConstants.BLANK;
|
||||
}
|
||||
return browserChannel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* eslint-disable no-tabs */
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
import fs from "fs";
|
||||
import path from 'path';
|
||||
import CommonConstants from "../constants/CommonConstants";
|
||||
import SuiteTemplate from "../template/SuiteTemplate";
|
||||
import CLIUtil from "../utils/CLIUtil";
|
||||
import ExcelUtil from "../utils/ExcelUtil";
|
||||
|
||||
export default class SuiteManager {
|
||||
public static createSuite() {
|
||||
const sheet = CLIUtil.getValueOf("SHEET");
|
||||
this.deleteFiles(CommonConstants.TEST_FOLDER_PATH);
|
||||
let testList = CommonConstants.BLANK;
|
||||
for (const { TestName, Mode } of ExcelUtil.getSuiteTests(sheet)) {
|
||||
let modeOfRun = CommonConstants.BLANK;
|
||||
if (Mode !== undefined && Mode !== null && Mode !== CommonConstants.BLANK) {
|
||||
modeOfRun = `\n\ttest.describe.configure({ mode: '${Mode.toLowerCase()}' });`;
|
||||
}
|
||||
testList += `\ntest.describe("${TestName}", () => {${modeOfRun}
|
||||
require("./${TestName}.spec.ts");
|
||||
});`;
|
||||
}
|
||||
fs.writeFileSync(`${CommonConstants.TEST_FOLDER_PATH}${sheet}${CommonConstants.TEST_SUITE_FILE_FORMAT}`,
|
||||
SuiteTemplate.getTemplate(sheet, testList));
|
||||
console.log(" Completed!! ");
|
||||
}
|
||||
|
||||
private static deleteFiles(directory: string) {
|
||||
const files = fs.readdirSync(directory);
|
||||
for (const file of files) {
|
||||
if (file.includes(CommonConstants.TEST_SUITE_FILE_FORMAT)) { fs.unlinkSync(path.join(directory, file)); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SuiteManager.createSuite();
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Page } from "@playwright/test";
|
||||
import RequestHeader from "./RequestHeader";
|
||||
import RESTRequest from "./RESTRequest";
|
||||
import SOAPRequest from "./SOAPRequest";
|
||||
|
||||
export default class APIActions {
|
||||
constructor(private page: Page) { }
|
||||
/**
|
||||
* Returns REST Request instance
|
||||
* @returns
|
||||
*/
|
||||
public get rest(): RESTRequest {
|
||||
return new RESTRequest(this.page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns SOAP Request instance
|
||||
* @returns
|
||||
*/
|
||||
public get soap(): SOAPRequest {
|
||||
return new SOAPRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Request header instance
|
||||
* @returns
|
||||
*/
|
||||
public get header(): RequestHeader {
|
||||
return new RequestHeader();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import { test, Page, APIResponse } from '@playwright/test';
|
||||
import fs from 'fs';
|
||||
import fetchToCurl from 'fetch-to-curl';
|
||||
import CommonConstants from '../../constants/CommonConstants';
|
||||
import StringUtil from '../../utils/StringUtil';
|
||||
import RESTResponse from "./RESTResponse";
|
||||
|
||||
export default class RESTRequest {
|
||||
constructor(private page: Page) { }
|
||||
/**
|
||||
* Creates request body from JSON file by replacing the input parameters
|
||||
* @param jsonFileName
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
public async createRequestBody(jsonFileName: string, data: any): Promise<string> {
|
||||
let json = fs.readFileSync(CommonConstants.REST_JSON_REQUEST_PATH + jsonFileName, 'utf-8');
|
||||
json = StringUtil.formatStringValue(json, data);
|
||||
return json;
|
||||
}
|
||||
/**
|
||||
* Make POST request and return response
|
||||
* @param endPoint
|
||||
* @param requestHeader
|
||||
* @param jsonAsString
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public async post(endPoint: string, requestHeader: any, jsonAsString: string,
|
||||
description: string): Promise<RESTResponse> {
|
||||
const headersAsJson = JSON.parse(JSON.stringify(requestHeader));
|
||||
let restResponse: RESTResponse;
|
||||
await test.step(`Making POST request for ${description}`, async () => {
|
||||
this.printRequest(endPoint, headersAsJson, jsonAsString, 'post');
|
||||
const response = await this.page.request.post(endPoint,
|
||||
{ headers: headersAsJson, data: JSON.parse(jsonAsString) });
|
||||
restResponse = await this.setRestResponse(response, description);
|
||||
});
|
||||
return restResponse;
|
||||
}
|
||||
/**
|
||||
* Sets the API Response into RestResponse object
|
||||
* @param response
|
||||
* @param description
|
||||
* @returns RestResponse object
|
||||
*/
|
||||
private async setRestResponse(response: APIResponse, description: string): Promise<RESTResponse> {
|
||||
const body = await response.text();
|
||||
const headers = response.headers();
|
||||
const statusCode = response.status();
|
||||
const restResponse: RESTResponse = new RESTResponse(headers, body, statusCode, description);
|
||||
console.log(`Response body: ${JSON.stringify(JSON.parse(body), undefined, 2)}`);
|
||||
return restResponse;
|
||||
}
|
||||
/**
|
||||
* Make Get request and return response
|
||||
* @param endPoint
|
||||
* @param requestHeader
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public async get(endPoint: string, requestHeader: any, description: string): Promise<RESTResponse> {
|
||||
const headersAsJson = JSON.parse(JSON.stringify(requestHeader));
|
||||
let restResponse: RESTResponse;
|
||||
await test.step(`Making GET request for ${description}`, async () => {
|
||||
this.printRequest(endPoint, headersAsJson, null, 'get');
|
||||
const response = await this.page.request.get(endPoint, { headers: headersAsJson });
|
||||
restResponse = await this.setRestResponse(response, description);
|
||||
});
|
||||
return restResponse;
|
||||
}
|
||||
/**
|
||||
* Make Put request and return response
|
||||
* @param endPoint
|
||||
* @param requestHeader
|
||||
* @param jsonAsString
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public async put(endPoint: string, requestHeader: any, jsonAsString: any,
|
||||
description: string): Promise<RESTResponse> {
|
||||
const headersAsJson = JSON.parse(JSON.stringify(requestHeader));
|
||||
let restResponse: RESTResponse;
|
||||
await test.step(`Making PUT request for ${description}`, async () => {
|
||||
this.printRequest(endPoint, headersAsJson, jsonAsString, 'put');
|
||||
const response = await this.page.request.put(endPoint,
|
||||
{ headers: headersAsJson, data: JSON.parse(jsonAsString) });
|
||||
restResponse = await this.setRestResponse(response, description);
|
||||
});
|
||||
return restResponse;
|
||||
}
|
||||
/**
|
||||
* Make Patch request and return response
|
||||
* @param endPoint
|
||||
* @param requestHeader
|
||||
* @param jsonAsString
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public async patch(endPoint: string, requestHeader: any, jsonAsString: any,
|
||||
description: string): Promise<RESTResponse> {
|
||||
const headersAsJson = JSON.parse(JSON.stringify(requestHeader));
|
||||
let restResponse: RESTResponse;
|
||||
await test.step(`Making PATCH request for ${description}`, async () => {
|
||||
this.printRequest(endPoint, headersAsJson, jsonAsString, 'patch');
|
||||
const response = await this.page.request.patch(endPoint,
|
||||
{ headers: headersAsJson, data: JSON.parse(jsonAsString) });
|
||||
restResponse = await this.setRestResponse(response, description);
|
||||
});
|
||||
return restResponse;
|
||||
}
|
||||
/**
|
||||
* Make Delete request and return response
|
||||
* @param endPoint
|
||||
* @param requestHeader
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public async delete(endPoint: string, requestHeader: any, description: string): Promise<RESTResponse> {
|
||||
const headersAsJson = JSON.parse(JSON.stringify(requestHeader));
|
||||
let restResponse: RESTResponse;
|
||||
await test.step(`Making DELETE request for ${description}`, async () => {
|
||||
this.printRequest(endPoint, headersAsJson, null, 'delete');
|
||||
const response = await this.page.request.delete(endPoint, { headers: headersAsJson });
|
||||
restResponse = await this.setRestResponse(response, description);
|
||||
});
|
||||
return restResponse;
|
||||
}
|
||||
/**
|
||||
* Prints the API request on console in curl format
|
||||
* @param endPoint
|
||||
* @param requestHeader
|
||||
* @param jsonRequestBody
|
||||
* @param method
|
||||
*/
|
||||
private printRequest(endPoint: string, requestHeader: any, jsonRequestBody: string, method: string) {
|
||||
let requestBody = jsonRequestBody;
|
||||
if (jsonRequestBody !== null) {
|
||||
requestBody = JSON.stringify(JSON.parse(jsonRequestBody), undefined, 2);
|
||||
}
|
||||
console.log("Request: ", fetchToCurl({
|
||||
url: endPoint,
|
||||
headers: requestHeader,
|
||||
body: requestBody,
|
||||
method: method,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import test from "@playwright/test";
|
||||
import jp from "jsonpath";
|
||||
|
||||
export default class RESTResponse {
|
||||
public constructor(private headers: any, private body: string, private status: number,
|
||||
private description: string) { }
|
||||
|
||||
/**
|
||||
* Get content of tag in response body using JSON path
|
||||
* @param jsonPath
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public async getTagContentByJsonPath(jsonPath: string, description: string): Promise<string> {
|
||||
let text: string;
|
||||
await test.step(`Getting content of ${description}`, async () => {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
text = jp.query(JSON.parse(this.body), jsonPath)[0];
|
||||
});
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header value by header key
|
||||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
public async getHeaderValueByKey(key: string): Promise<string> {
|
||||
let value: string;
|
||||
await test.step(`Getting header value of ${key}`, async () => {
|
||||
const jsonHeaders = await JSON.parse(JSON.stringify(this.headers));
|
||||
value = jsonHeaders[key];
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response status code
|
||||
* @returns
|
||||
*/
|
||||
public async getStatusCode(): Promise<number> {
|
||||
let status: number;
|
||||
await test.step(`Getting status code of ${this.description}`, async () => {
|
||||
status = this.status;
|
||||
});
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response body
|
||||
* @returns
|
||||
*/
|
||||
public async getBody(): Promise<string> {
|
||||
let body: string;
|
||||
await test.step(`Getting response body of ${this.description}`, async () => {
|
||||
body = this.body;
|
||||
});
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response headers
|
||||
* @returns
|
||||
*/
|
||||
public async getHeaders(): Promise<string> {
|
||||
let headers: string;
|
||||
await test.step(`Getting response Headers of ${this.description}`, async () => {
|
||||
headers = this.headers;
|
||||
});
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export default class RequestHeader {
|
||||
private map = new Map<string, any>();
|
||||
|
||||
public set(key: string, value: any): RequestHeader {
|
||||
this.map.set(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public get() {
|
||||
return Object.fromEntries(this.map);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import test from "@playwright/test";
|
||||
import soapRequest from "easy-soap-request";
|
||||
import format from "xml-formatter";
|
||||
import fs from 'fs';
|
||||
import SOAPResponse from "./SOAPResponse";
|
||||
import StringUtil from "../../utils/StringUtil";
|
||||
import CommonConstants from "../../constants/CommonConstants";
|
||||
|
||||
export default class SOAPRequest {
|
||||
/**
|
||||
* Creates request body by replacing the input parameters
|
||||
* @param xmlFileName
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
private async createRequestBody(xmlFileName: string, data: any): Promise<string> {
|
||||
let xml = fs.readFileSync(CommonConstants.SOAP_XML_REQUEST_PATH + xmlFileName, 'utf-8');
|
||||
xml = StringUtil.formatStringValue(xml, data);
|
||||
console.log(`SOAP request : \n${format(xml, { collapseContent: true })}`);
|
||||
return xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make POST request and return response
|
||||
* @param endPoint
|
||||
* @param requestHeader
|
||||
* @param fileName
|
||||
* @param gData
|
||||
* @param data
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public async post(endPoint: string, requestHeader: any, fileName: string,
|
||||
requestData : any, description: string): Promise<SOAPResponse> {
|
||||
let soapResponse: SOAPResponse;
|
||||
await test.step(`Making post request for ${description}`, async () => {
|
||||
const url = process.env.SOAP_API_BASE_URL + endPoint;
|
||||
console.log(`URL: ${url}`);
|
||||
const xml = await this.createRequestBody(fileName, requestData);
|
||||
const { response } = await soapRequest({ url: url, headers: requestHeader, xml: xml });
|
||||
const { headers, body, statusCode } = response;
|
||||
soapResponse = new SOAPResponse(headers, body, statusCode, description);
|
||||
console.log(`SOAP Response: \n${format(body, { collapseContent: true })}`);
|
||||
});
|
||||
return soapResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import test from "@playwright/test";
|
||||
import XMLParserUtil from "../../utils/XMLParserUtil";
|
||||
|
||||
export default class SOAPResponse {
|
||||
public constructor(private headers: any, private body: any, private status: number, private description: string) { }
|
||||
/**
|
||||
* Get content of tag in response body using xpath
|
||||
* @param xPathExpression xpath for the tag
|
||||
* @param description
|
||||
*/
|
||||
public async getTagContentByXpath(xPathExpression: string, description: string): Promise<string> {
|
||||
let text: string;
|
||||
await test.step(`Getting tag value of action ${description}`, async () => {
|
||||
text = XMLParserUtil.getTagContentByXpath(this.body, xPathExpression);
|
||||
});
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value of attribute in response body using xpath
|
||||
* @param xPathExpression xpath for the attribute
|
||||
* @param description
|
||||
*/
|
||||
public async getAttributeValueByXpath(xPathExpression: string, description: string): Promise<string> {
|
||||
let text: string;
|
||||
await test.step(`Getting attribute value of action ${description}`, async () => {
|
||||
text = XMLParserUtil.getAttributeValueByXpath(this.body, xPathExpression);
|
||||
});
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header value by header key
|
||||
* @param key
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public async getHeaderValueByKey(key: string): Promise<string> {
|
||||
let value:string;
|
||||
await test.step(`Getting header value of ${key}`, async () => {
|
||||
const jsonHeaders = await JSON.parse(JSON.stringify(this.headers));
|
||||
value = jsonHeaders[key];
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response status code
|
||||
* @returns
|
||||
*/
|
||||
public async getStatusCode(): Promise<number> {
|
||||
let status:number;
|
||||
await test.step(`Getting status code of ${this.description}`, async () => {
|
||||
status = this.status;
|
||||
});
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response body
|
||||
* @returns
|
||||
*/
|
||||
public async getBody(): Promise<string> {
|
||||
let body:string;
|
||||
await test.step(`Getting response body of ${this.description}`, async () => {
|
||||
body = this.body;
|
||||
});
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response headers
|
||||
* @returns
|
||||
*/
|
||||
public async getHeaders(): Promise<string> {
|
||||
let headers:string;
|
||||
await test.step(`Getting response Headers of ${this.description}`, async () => {
|
||||
headers = JSON.stringify(this.headers);
|
||||
});
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Page } from "@playwright/test";
|
||||
|
||||
export default class AlertActions {
|
||||
constructor(private page: Page) {}
|
||||
|
||||
/**
|
||||
* Accept alert and return alert message
|
||||
* @param promptText A text to enter in prompt. It is optional for alerts.
|
||||
* @returns alert message
|
||||
*/
|
||||
public async accept(promptText?: string): Promise<string> {
|
||||
return this.page.waitForEvent("dialog").then(async (dialog) => {
|
||||
if (dialog.type() === "prompt") {
|
||||
await dialog.accept(promptText);
|
||||
} else {
|
||||
await dialog.accept();
|
||||
}
|
||||
return dialog.message().trim();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss alert and return alert message
|
||||
* @returns alert message
|
||||
*/
|
||||
public async dismiss(): Promise<string> {
|
||||
return this.page.waitForEvent("dialog").then(async (d) => {
|
||||
await d.dismiss();
|
||||
return d.message().trim();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { test, Locator } from "@playwright/test";
|
||||
|
||||
export default class CheckBoxActions {
|
||||
private locator: Locator;
|
||||
private description: string;
|
||||
|
||||
/**
|
||||
* Sets the locator with description
|
||||
* @param locator
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public setLocator(locator: Locator, description: string): CheckBoxActions {
|
||||
this.locator = locator;
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* check checkbox or radio button
|
||||
*/
|
||||
public async check() {
|
||||
await test.step(`Check ${this.description}`, async () => {
|
||||
await this.locator.check();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* uncheck checkbox or radio button
|
||||
*/
|
||||
public async uncheck() {
|
||||
await await test.step(`Uncheck ${this.description}`, async () => {
|
||||
await this.locator.uncheck();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the checkbox
|
||||
* @returns
|
||||
*/
|
||||
public async isChecked(): Promise<boolean> {
|
||||
let status: boolean;
|
||||
await test.step(`Checking status of checkbox ${this.description}`, async () => {
|
||||
const element = this.locator;
|
||||
await element.waitFor();
|
||||
status = await element.isChecked();
|
||||
},
|
||||
);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { test, Locator } from "@playwright/test";
|
||||
import HTMLConstants from "../../constants/HTMLConstants";
|
||||
|
||||
export default class DropDownActions {
|
||||
private locator: Locator;
|
||||
private description: string;
|
||||
|
||||
/**
|
||||
* Sets the locator with description
|
||||
* @param locator
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public setLocator(locator: Locator, description: string): DropDownActions {
|
||||
this.locator = locator;
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the dropdown by value
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
public async selectByValue(value: string) {
|
||||
await test.step(`Selecting value ${value} from ${this.description}`, async () => {
|
||||
await this.locator.selectOption({ value });
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the dropdown by Label
|
||||
* @param text
|
||||
* @returns
|
||||
*/
|
||||
public async selectByVisibleText(text: string) {
|
||||
await test.step(`Selecting text ${text} from ${this.description}`, async () => {
|
||||
await this.locator.selectOption({ label: text });
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the dropdown by index
|
||||
* @param index
|
||||
* @returns
|
||||
*/
|
||||
public async selectByIndex(index: number) {
|
||||
await test.step(`Selecting index ${index} of ${this.description}`, async () => {
|
||||
await this.locator.selectOption({ index });
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the options in dropdown
|
||||
* @param index
|
||||
* @returns
|
||||
*/
|
||||
public async getAllOptions(): Promise<string[]> {
|
||||
let selectOptions: string[];
|
||||
await test.step(
|
||||
`Getting all the options of ${this.description}`,
|
||||
async () => {
|
||||
selectOptions = await this.locator.locator(HTMLConstants.OPTION).allTextContents();
|
||||
},
|
||||
);
|
||||
return selectOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the selected options in dropdown
|
||||
* @param index
|
||||
* @returns
|
||||
*/
|
||||
public async getAllSelectedOptions(): Promise<string[]> {
|
||||
let selectOptions: string[];
|
||||
await test.step(
|
||||
`Getting all the selected options of ${this.description}`,
|
||||
async () => {
|
||||
selectOptions = await this.locator
|
||||
.locator(HTMLConstants.SELECTED_OPTION)
|
||||
.allTextContents();
|
||||
},
|
||||
);
|
||||
return selectOptions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { test, Locator } from "@playwright/test";
|
||||
import UIElementActions from "./UIElementActions";
|
||||
|
||||
export default class EditBoxActions extends UIElementActions {
|
||||
/**
|
||||
* Sets the selector with description
|
||||
* @param selector
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public setEditBox(selector: string, description: string): EditBoxActions {
|
||||
this.setElement(selector, description);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the locator with description
|
||||
* @param locator
|
||||
* @returns
|
||||
*/
|
||||
public setLocator(locator: Locator, description: string): EditBoxActions {
|
||||
super.setLocator(locator, description);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear and enter text
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
public async fill(value: string) {
|
||||
await test.step(`Entering ${this.description} as ${value}`, async () => {
|
||||
await this.getLocator().fill(value);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Types the value to text field
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
public async type(value: string) {
|
||||
await test.step(`Typing ${this.description} as ${value}`, async () => {
|
||||
await this.getLocator().type(value);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter text and hit tab key
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
public async fillAndTab(value: string) {
|
||||
await test.step(`Entering ${this.description} as ${value} and Tab`, async () => {
|
||||
await this.getLocator().fill(value);
|
||||
await this.getLocator().press("Tab");
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Typing text and hit tab key
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
public async typeAndTab(value: string) {
|
||||
await test.step(`Entering ${this.description} as ${value} and Tab`, async () => {
|
||||
await this.getLocator().type(value);
|
||||
await this.getLocator().press("Tab");
|
||||
});
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
import { test, Page } from "@playwright/test";
|
||||
import CommonConstants from "../../constants/CommonConstants";
|
||||
import HTMLConstants from "../../constants/HTMLConstants";
|
||||
import AlertActions from "./AlertActions";
|
||||
import CheckBoxActions from "./CheckBoxActions";
|
||||
import DropDownActions from "./DropDownActions";
|
||||
import EditBoxActions from "./EditBoxActions";
|
||||
import UIElementActions from "./UIElementActions";
|
||||
|
||||
export default class UIActions {
|
||||
private elementAction: UIElementActions;
|
||||
private editBoxAction: EditBoxActions;
|
||||
private checkboxAction: CheckBoxActions;
|
||||
private dropdownAction: DropDownActions;
|
||||
private alertAction: AlertActions;
|
||||
|
||||
constructor(private page: Page) {
|
||||
this.elementAction = new UIElementActions(page);
|
||||
this.editBoxAction = new EditBoxActions(page);
|
||||
this.checkboxAction = new CheckBoxActions();
|
||||
this.dropdownAction = new DropDownActions();
|
||||
this.alertAction = new AlertActions(this.page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns page object
|
||||
* @returns
|
||||
*/
|
||||
public getPage(): Page {
|
||||
return this.page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the page
|
||||
* @param page
|
||||
*/
|
||||
public setPage(page: Page) {
|
||||
this.page = page;
|
||||
this.elementAction = new UIElementActions(page);
|
||||
this.editBoxAction = new EditBoxActions(page);
|
||||
this.alertAction = new AlertActions(this.page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close page
|
||||
* @returns
|
||||
*/
|
||||
public closePage() {
|
||||
this.page.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of Alert
|
||||
* @returns
|
||||
*/
|
||||
public alert() {
|
||||
return this.alertAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of editbox actions
|
||||
* @param selector
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public editBox(selector: string, description: string) {
|
||||
return this.editBoxAction.setEditBox(selector, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of UIElements actions
|
||||
* @param selector
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public element(selector: string, description: string) {
|
||||
return this.elementAction.setElement(selector, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of Dropdown actions
|
||||
* @param selector
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public dropdown(selector: string, description: string) {
|
||||
return this.dropdownAction.setLocator(
|
||||
this.elementAction.setElement(selector, description).getLocator(),
|
||||
description,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of CheckBox actions
|
||||
* @param selector
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public checkbox(selector: string, description: string) {
|
||||
return this.checkboxAction.setLocator(
|
||||
this.elementAction.setElement(selector, description).getLocator(),
|
||||
description,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to specified URL
|
||||
* @param URL
|
||||
* @param description
|
||||
*/
|
||||
public async goto(URL: string, description: string) {
|
||||
await test.step(`Navigate to ${description}`, async () => {
|
||||
await this.page.goto(URL);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to previous URL
|
||||
* @param description
|
||||
*/
|
||||
public async goBack(description: string) {
|
||||
await test.step(`Go to the previous ${description}`, async () => {
|
||||
await this.page.goBack();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to next URL
|
||||
* @param description
|
||||
*/
|
||||
public async goForward(description: string) {
|
||||
await test.step(`Go to the next ${description}`, async () => {
|
||||
await this.page.goForward();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Page Refresh
|
||||
*/
|
||||
public async pageRefresh() {
|
||||
await test.step(`Page Refresh`, async () => {
|
||||
await this.page.reload();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Press a key on web page
|
||||
* @param key
|
||||
* @param description
|
||||
*/
|
||||
public async keyPress(key: string, description: string) {
|
||||
await test.step(`Pressing ${description}`, async () => {
|
||||
await this.page.keyboard.press(key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the main frame navigation and returns the main resource response.
|
||||
*/
|
||||
public async waitForNavigation() {
|
||||
await test.step(`Waiting for navigation`, async () => {
|
||||
await this.page.waitForNavigation();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when the required load state has been reached.
|
||||
*/
|
||||
public async waitForLoadState() {
|
||||
await test.step(`Waiting for load event`, async () => {
|
||||
await this.page.waitForLoadState();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when the required dom content is in loaded state.
|
||||
*/
|
||||
public async waitForDomContentLoaded() {
|
||||
await test.step(`Waiting for load event`, async () => {
|
||||
await this.page.waitForLoadState("domcontentloaded", { timeout: 5000 });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the handle of the new window
|
||||
* @param selector
|
||||
* @param description
|
||||
*/
|
||||
public async switchToNewWindow(
|
||||
selector: string,
|
||||
description: string,
|
||||
): Promise<Page> {
|
||||
let [newPage] = [this.page];
|
||||
await test.step(`Opening ${description} Window`, async () => {
|
||||
[newPage] = await Promise.all([
|
||||
this.page.context().waitForEvent("page"),
|
||||
await this.elementAction.setElement(selector, description).click(),
|
||||
]);
|
||||
await newPage.waitForLoadState("domcontentloaded");
|
||||
});
|
||||
return newPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks the an element, accepts the alert and returns the alert message
|
||||
* @param selector selector of the element
|
||||
* @param description description of element
|
||||
* @returns alert message
|
||||
*/
|
||||
public async acceptAlertOnElementClick(
|
||||
selector: string,
|
||||
description: string,
|
||||
): Promise<string> {
|
||||
const message = this.alert().accept();
|
||||
return this.handleAlert(selector, description, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks the an element, dismisses the alert and returns the alert message
|
||||
* @param selector selector of the element
|
||||
* @param description description of element
|
||||
* @returns alert message
|
||||
*/
|
||||
public async dismissAlertOnElementClick(
|
||||
selector: string,
|
||||
description: string,
|
||||
): Promise<string> {
|
||||
const message = this.alert().dismiss();
|
||||
return this.handleAlert(selector, description, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks the an element, accepts the alert prompt and returns the alert message
|
||||
* @param selector selector of the element
|
||||
* @param description description of element
|
||||
* @param promptText A text to enter in prompt.
|
||||
* @returns alert message
|
||||
*/
|
||||
public async acceptPromptOnElementClick(
|
||||
selector: string,
|
||||
description: string,
|
||||
promptText: string,
|
||||
): Promise<string> {
|
||||
const message = this.alert().accept(promptText);
|
||||
return this.handleAlert(selector, description, message);
|
||||
}
|
||||
|
||||
private async handleAlert(
|
||||
selector: string,
|
||||
description: string,
|
||||
message: Promise<string>,
|
||||
) {
|
||||
await this.elementAction.setElement(selector, description).click();
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the page Title
|
||||
* @returns
|
||||
*/
|
||||
public async getPageTitle(): Promise<string> {
|
||||
let title: string;
|
||||
await test.step(`Getting Page Title`, async () => {
|
||||
title = await this.page.title();
|
||||
});
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the file and returns the downloaded file name
|
||||
* @param selector element that results in file download
|
||||
* @param description description of the element
|
||||
* @returns downloaded file name
|
||||
*/
|
||||
public async downloadFile(selector: string, description: string): Promise<string> {
|
||||
let fileName: string;
|
||||
await test.step(`Downloading ${description} file`, async () => {
|
||||
const [download] = await Promise.all([
|
||||
this.page.waitForEvent('download'),
|
||||
await this.page.locator(selector).click({ modifiers: ["Alt"] }),
|
||||
]);
|
||||
fileName = download.suggestedFilename();
|
||||
const filePath = `${CommonConstants.DOWNLOAD_PATH}${fileName}`;
|
||||
await download.saveAs(filePath);
|
||||
await download.delete();
|
||||
});
|
||||
return fileName;
|
||||
}
|
||||
/**
|
||||
* Pause the execution in seconds
|
||||
* @param sec
|
||||
*/
|
||||
public async pauseInSecs(sec: number) {
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
return new Promise((resolve) => setTimeout(resolve, sec * CommonConstants.ONE_THOUSAND));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait For Page loading image to disappear
|
||||
* @param page
|
||||
*/
|
||||
public async waitForLoadingImage() {
|
||||
await test.step("Waiting for Loading Image to disappear", async () => {
|
||||
try {
|
||||
await this.page.locator(HTMLConstants.LOADING_IMAGE).waitFor({
|
||||
state: "visible",
|
||||
timeout: CommonConstants.ONE_THOUSAND * CommonConstants.THREE,
|
||||
});
|
||||
} catch (error) {
|
||||
// console.log("Loading Image was not displayed");
|
||||
}
|
||||
await this.page.locator(HTMLConstants.LOADING_IMAGE).waitFor({ state: "hidden" });
|
||||
await this.pauseInSecs(CommonConstants.HALF);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
import { test, Locator, Page } from "@playwright/test";
|
||||
|
||||
export default class UIElementActions {
|
||||
protected locator: Locator;
|
||||
protected description: string;
|
||||
protected selector: string;
|
||||
|
||||
constructor(private page: Page) { }
|
||||
|
||||
/**
|
||||
* Returns the first locator
|
||||
* @returns
|
||||
*/
|
||||
public getLocator(): Locator {
|
||||
return this.locator.first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the all the locators
|
||||
* @returns
|
||||
*/
|
||||
public getLocators(): Locator {
|
||||
return this.locator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the locator using the selector *
|
||||
* @param selector
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public setElement(selector: string, description: string): UIElementActions {
|
||||
this.selector = selector;
|
||||
this.locator = this.page.locator(this.selector);
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the locator with description
|
||||
* @param locator
|
||||
* @param description
|
||||
* @returns
|
||||
*/
|
||||
public setLocator(locator: Locator, description: string): UIElementActions {
|
||||
this.locator = locator;
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on element
|
||||
* @returns
|
||||
*/
|
||||
public async click() {
|
||||
await test.step(`Clicking on ${this.description}`, async () => {
|
||||
await this.getLocator().click();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Double click on element
|
||||
* @returns
|
||||
*/
|
||||
public async doubleClick() {
|
||||
await test.step(`Double Clicking ${this.description}`, async () => {
|
||||
await this.getLocator().dblclick();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* scroll element into view, unless it is completely visible
|
||||
* @returns
|
||||
*/
|
||||
public async scrollIntoView() {
|
||||
await test.step(`Scroll to element ${this.description}`, async () => {
|
||||
await this.getLocator().scrollIntoViewIfNeeded();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for element to be invisible
|
||||
* @returns
|
||||
*/
|
||||
public async waitTillInvisible() {
|
||||
await test.step(`Waiting for ${this.description} to be invisible`, async () => {
|
||||
await this.getLocator().waitFor({ state: "hidden" });
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait for element not to be present in DOM
|
||||
* @returns
|
||||
*/
|
||||
public async waitTillDetached() {
|
||||
await test.step(`Wait for ${this.description} to be detached from DOM`, async () => {
|
||||
await this.getLocator().waitFor({ state: "detached" });
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait for element to be visible
|
||||
* @param wait time for element is visible
|
||||
* @returns
|
||||
*/
|
||||
public async waitTillVisible(sec: number) {
|
||||
await test.step(`Wait for ${this.description} to be visible in DOM`, async () => {
|
||||
await this.getLocator().waitFor({ state: "visible", timeout: sec * 1000 });
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait for element to be attached to DOM
|
||||
* @returns
|
||||
*/
|
||||
public async waitForPresent() {
|
||||
await test.step(`Wait for ${this.description} to attach to DOM`, async () => {
|
||||
await this.getLocator().waitFor({ state: "attached" });
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method hovers over the element
|
||||
*/
|
||||
public async hover() {
|
||||
await test.step(`Hovering on ${this.description}`, async () => {
|
||||
await this.getLocator().hover();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns input.value for <input> or <textarea> or <select> element.
|
||||
* @returns
|
||||
*/
|
||||
public async getInputValue(): Promise<string> {
|
||||
let value: string;
|
||||
await test.step(`Getting input value of ${this.description}`, async () => {
|
||||
const element = this.getLocator();
|
||||
await element.waitFor();
|
||||
value = await element.inputValue();
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text content
|
||||
* @returns
|
||||
*/
|
||||
public async getTextContent(): Promise<string> {
|
||||
let content: string;
|
||||
await test.step(`Getting text content of ${this.description}`, async () => {
|
||||
const element = this.getLocator();
|
||||
await element.waitFor();
|
||||
content = (await element.textContent()).trim();
|
||||
});
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Attribute value
|
||||
* @param attributeName
|
||||
* @returns
|
||||
*/
|
||||
public async getAttribute(attributeName: string): Promise<string> {
|
||||
let value: string;
|
||||
await test.step(`Getting attribute value of ${this.description}`, async () => {
|
||||
const element = this.getLocator();
|
||||
await element.waitFor();
|
||||
value = (await element.getAttribute(attributeName)).trim();
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get innerHTML
|
||||
* @returns
|
||||
*/
|
||||
public async getInnerHTML(): Promise<string> {
|
||||
let text: string;
|
||||
await test.step(`Get innerHTML of ${this.description}`, async () => {
|
||||
const element = this.getLocator();
|
||||
await element.waitFor();
|
||||
text = (await element.innerHTML()).trim();
|
||||
});
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get inner text
|
||||
* @returns
|
||||
*/
|
||||
public async getInnerText(): Promise<string> {
|
||||
let text: string;
|
||||
await test.step(`Get inner text of ${this.description}`, async () => {
|
||||
const element = this.getLocator();
|
||||
await element.waitFor();
|
||||
text = (await element.innerText()).trim();
|
||||
});
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if element is editable
|
||||
* @returns Promise<boolean>
|
||||
*/
|
||||
public async isEditable(): Promise<boolean> {
|
||||
let status: boolean;
|
||||
await test.step(`Checking if ${this.description} is editable`, async () => {
|
||||
const element = this.getLocator();
|
||||
await element.waitFor();
|
||||
status = await element.isEditable();
|
||||
});
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if element is enabled
|
||||
* @returns Promise<boolean>
|
||||
*/
|
||||
public async isEnabled(): Promise<boolean> {
|
||||
let status: boolean;
|
||||
await test.step(`Checking if ${this.description} is enabled`, async () => {
|
||||
const element = this.getLocator();
|
||||
await element.waitFor();
|
||||
status = await element.isEnabled();
|
||||
});
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if element is visible
|
||||
* @param wait time for element to be visible
|
||||
* @returns Promise<boolean>
|
||||
*/
|
||||
public async isVisible(sec: number): Promise<boolean> {
|
||||
let visibility: boolean;
|
||||
await test.step(`Checking if ${this.description} is visible`, async () => {
|
||||
try {
|
||||
visibility = await this.getLocator().isVisible({ timeout: sec * 1000 });
|
||||
} catch (error) {
|
||||
visibility = false;
|
||||
}
|
||||
});
|
||||
return visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Press a key on web element
|
||||
* @param key
|
||||
*/
|
||||
public async keyPress(key: string) {
|
||||
await test.step(`Pressing ${this.description}`, async () => {
|
||||
await this.getLocator().press(key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the text Content
|
||||
* @returns
|
||||
*/
|
||||
public async getAllTextContent(): Promise<string[]> {
|
||||
let content: string[];
|
||||
await test.step(`Getting all the text content of ${this.description}`, async () => {
|
||||
const element = this.getLocators();
|
||||
await element.first().waitFor();
|
||||
content = await element.allTextContents();
|
||||
});
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of
|
||||
* @returns
|
||||
*/
|
||||
public async getCount(): Promise<number> {
|
||||
let count: number;
|
||||
await test.step(`Getting the count of ${this.description}`, async () => {
|
||||
count = await this.getLocators().count();
|
||||
});
|
||||
return count;
|
||||
}
|
||||
/**
|
||||
* Performs mouse click action on the element
|
||||
* @returns
|
||||
*/
|
||||
public async mouseClick() {
|
||||
await test.step(`Clicking on ${this.description}`, async () => {
|
||||
await this.getLocator().scrollIntoViewIfNeeded();
|
||||
const box = await this.getLocator().boundingBox();
|
||||
await this.page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Click on element using js
|
||||
* @returns
|
||||
*/
|
||||
public async jsClick() {
|
||||
await test.step(`Clicking on ${this.description}`, async () => {
|
||||
const ele = this.getLocator();
|
||||
await ele.waitFor();
|
||||
await ele.evaluate((node: HTMLElement) => { node.click(); });
|
||||
});
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
export default class Assert {
|
||||
/**
|
||||
* To verify that condition passed as input is true
|
||||
* @param condition - boolean condition
|
||||
* @param description - description of element that is being validated
|
||||
* @param softAssert - for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertTrue(condition: boolean, description: string, softAssert = false) {
|
||||
await test.step(`Verifying that ${description} is true`, async () => {
|
||||
try {
|
||||
expect(condition, `Expected is 'True' & Actual is '${condition}'`).toBeTruthy();
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* To verify that value1 contains value2
|
||||
* @param value1 - string input
|
||||
* @param value2 - should be present in value1
|
||||
* @param description - description of element that is being validated
|
||||
* @param softAssert - for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertContains(value1: string, value2: string, description: string, softAssert = false) {
|
||||
await test.step(`Verifying that ${description} contains text '${value2}'`, async () => {
|
||||
try {
|
||||
expect(value1, `'${value1}' is expected to CONTAIN '${value2}'`).toContain(value2);
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To verify that value1 contains value1 ignoring case
|
||||
* @param value1 - string input
|
||||
* @param value2 - should be present in value1
|
||||
* @param description - description of element that is being validated
|
||||
* @param softAssert - for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertContainsIgnoreCase(value1: string, value2: string, description: string,
|
||||
softAssert = false) {
|
||||
await test.step(`Verifying that ${description} contains text '${value2}'`, async () => {
|
||||
try {
|
||||
expect(value1.toLowerCase(), `'${value1}' is expected to CONTAIN '${value2}'`).toContain(value2.toLowerCase());
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To verify that actual contains expected ignoring case
|
||||
* @param actual - string input
|
||||
* @param expected - string input
|
||||
* @param description - description of element that is being validated
|
||||
* @param softAssert - for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertEqualsIgnoreCase(actual: string, expected: string, description: string,
|
||||
softAssert = false) {
|
||||
await test.step(`Verifying that ${description} has text ${expected}`, async () => {
|
||||
try {
|
||||
expect(actual.toLowerCase(), `Expected '${expected}' should be EQUAL to Actual '${actual}'`)
|
||||
.toEqual(expected.toLowerCase());
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To verify actual equals expected
|
||||
* @param value1 any object
|
||||
* @param value2 any object to compare
|
||||
* @param description object description
|
||||
* @param softAssert for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertEquals(actual: any, expected: any, description: string, softAssert = false) {
|
||||
await test.step(`Verifying that ${description} has text ${expected}`, async () => {
|
||||
try {
|
||||
expect(actual, `Expected '${expected}' should be EQUAL to Actual '${actual}'`).toEqual(expected);
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To verify that actual passed as input is false
|
||||
* @param condition boolean
|
||||
* @param description description of element that is being validated
|
||||
* @param softAssert for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertFalse(condition: boolean, description: string, softAssert = false) {
|
||||
await test.step(`Verifying that ${description} is false`, async () => {
|
||||
try {
|
||||
expect(condition, `Expected is 'false' & Acutal is '${condition}'`).toBeFalsy();
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To verify that element not contains expected
|
||||
* @param actual any value
|
||||
* @param expected any value
|
||||
* @param description description of element that is being validated
|
||||
* @param softAssert for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertNotContains(actual: any, expected: any, description: string, softAssert = false) {
|
||||
await test.step(`Verifying that ${description} does not contain '${expected}'`, async () => {
|
||||
try {
|
||||
await expect(actual, `'${actual}' should NOT CONTAIN '${expected}'`).not.toContain(expected);
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To verify actual not equals to expected
|
||||
* @param actual any object
|
||||
* @param expected any object to compare
|
||||
* @param description object description
|
||||
* @param softAssert for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertNotEquals(actual: any, expected: any, description: string, softAssert = false) {
|
||||
await test.step(`Verifying that ${description} is not equals to ${expected}`, async () => {
|
||||
try {
|
||||
expect(actual, `Expected '${expected}' should NOT be EQUAL to Actual '${actual}'`).not.toEqual(expected);
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To verify value not equals to null
|
||||
* @param value any value
|
||||
* @param description description of the value
|
||||
* @param softAssert for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertNotNull(value: any, description: string, softAssert = false) {
|
||||
await test.step(`Verifying that ${description} is not null`, async () => {
|
||||
try {
|
||||
expect(value, `Expected is 'NOT null' & Actual is '${value}'`).not.toEqual(null);
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To validate that value is not null
|
||||
* @param value any value
|
||||
* @param description description of the element
|
||||
* @param softAssert for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertNull(value: any, description: string, softAssert = false) {
|
||||
await test.step(`Verifying that ${description} is equals to null`, async () => {
|
||||
try {
|
||||
expect(value, `Expected is 'null' & Actual is '${value}'`).toEqual(null);
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To validate that value is Undefined
|
||||
* @param value any value
|
||||
* @param description description of the element
|
||||
* @param softAssert for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertUndefined(value: any, description: string, softAssert = false) {
|
||||
await test.step(`Verifying that ${description} is undefined`, async () => {
|
||||
try {
|
||||
expect(value, `Expected is 'Undefined' & Actual is '${value}'`).toEqual(typeof undefined);
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To validate that element is empty
|
||||
* @param value any element
|
||||
* @param description description of the element
|
||||
* @param softAssert for soft asserts this has to be set to true, else this can be ignored
|
||||
*/
|
||||
public static async assertToBeEmpty(value: any, description: string, softAssert = false) {
|
||||
await test.step(`Verifying that ${description} is empty`, async () => {
|
||||
try {
|
||||
await expect(value, `Expected is 'Empty' & Actual is '${value}'`).toBeEmpty();
|
||||
} catch (error) {
|
||||
if (!softAssert) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { allure } from "allure-playwright";
|
||||
import os from "os";
|
||||
|
||||
export default class Allure {
|
||||
public static attachDetails(description: string, issue: string) {
|
||||
allure.description(description);
|
||||
allure.owner(os.userInfo().username);
|
||||
if (issue !== undefined && issue !== null && issue !== '') {
|
||||
allure.link(`${process.env.LINK}${issue}`, `ISSUE-${issue}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import HTMLReport from "jasmine-xml2html-converter";
|
||||
import dotenv from 'dotenv';
|
||||
import CommonConstants from "../constants/CommonConstants";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
export default class HTMLReporter {
|
||||
public static generate() {
|
||||
const testConfig = {
|
||||
reportTitle: CommonConstants.REPORT_TITLE,
|
||||
outputPath: CommonConstants.RESULTS_PATH,
|
||||
BASE_URL: process.env.BASE_URL,
|
||||
SOAP_API_BASE_URL: process.env.SOAP_API_BASE_URL,
|
||||
REST_API_BASE_URL: process.env.REST_API_BASE_URL,
|
||||
DB_CONFIG: process.env.DB_CONFIG,
|
||||
BROWSER: process.env.BROWSER,
|
||||
};
|
||||
new HTMLReport().from(CommonConstants.JUNIT_RESULTS_PATH, testConfig);
|
||||
console.log("Completed creating HTML Report");
|
||||
}
|
||||
}
|
||||
|
||||
HTMLReporter.generate();
|
||||
@@ -0,0 +1,11 @@
|
||||
export default class SuiteTemplate {
|
||||
public static getTemplate(sheet: string, testList: string) {
|
||||
const suiteTemplate = `/* eslint-disable no-tabs */
|
||||
/* eslint-disable import/extensions */
|
||||
/* eslint-disable global-require */
|
||||
import test from "@playwright/test";
|
||||
${testList}
|
||||
`;
|
||||
return suiteTemplate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export default class CLIUtil {
|
||||
/**
|
||||
* Gets the value of command line argument
|
||||
* @param argumentName
|
||||
* @returns
|
||||
*/
|
||||
public static getValueOf(argumentName: string): string {
|
||||
const argv = process.argv[2];
|
||||
if (argv === undefined) {
|
||||
throw new Error(`${argumentName} is not defined, please send ${argumentName} through CLI`);
|
||||
}
|
||||
if (argv.toUpperCase().includes(argumentName)) {
|
||||
return argv.split("=")[1];
|
||||
}
|
||||
throw new Error(`Please send command line argument ${argumentName} with value`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/* eslint-disable @typescript-eslint/no-shadow */
|
||||
/* eslint-disable global-require */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import * as sql from "mssql";
|
||||
import oracledb from "oracledb";
|
||||
import CommonConstants from "../constants/CommonConstants";
|
||||
import DBConstants from "../constants/DBConstants";
|
||||
|
||||
export default class DBUtil {
|
||||
/**
|
||||
* Executes the query on MSSQL database
|
||||
* @param dbConfig data base configuration
|
||||
* @param query to be executed
|
||||
* @returns record set
|
||||
*/
|
||||
public static async executeMSSQLQuery(dbConfig: string, query: string) {
|
||||
try {
|
||||
const pool = await sql.connect(`${dbConfig}${DBConstants.CERTIFICATE}`);
|
||||
const result = await pool.request().query(query);
|
||||
return { rows: result.recordset, rowsAffected: result.rowsAffected };
|
||||
} catch (err) {
|
||||
throw new Error(`Error while executing query\n${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query on Oracle database
|
||||
* @param dbConfig data base configuration
|
||||
* @param query to be executed
|
||||
* @returns record set
|
||||
*/
|
||||
public static async executeOracleQuery(dbConfig: string, query: string) {
|
||||
const configs = dbConfig.split(CommonConstants.SEMICOLON);
|
||||
const config = {
|
||||
user: configs[0].replace(DBConstants.USER, CommonConstants.BLANK).trim(),
|
||||
password: configs[1].replace(DBConstants.PASSWORD, CommonConstants.BLANK).trim(),
|
||||
connectString: configs[2].replace(DBConstants.CONNECTION_STRING, CommonConstants.BLANK).trim(),
|
||||
};
|
||||
let connection: oracledb.Connection;
|
||||
try {
|
||||
connection = await oracledb.getConnection(config);
|
||||
const result = await connection.execute(query);
|
||||
return { rows: result.rows, rowsAffected: result.rowsAffected };
|
||||
} catch (err) {
|
||||
throw new Error(`Error while executing query\n${err.message}`);
|
||||
} finally {
|
||||
if (connection) {
|
||||
try {
|
||||
await connection.close();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query on DB2 database
|
||||
* @param dbConfig data base configuration
|
||||
* @param query to be executed
|
||||
* @returns record set
|
||||
*/
|
||||
public static async executeDB2Query(dbConfig: string, query: string) {
|
||||
const ibmdb = require('ibm_db');
|
||||
let connection: any;
|
||||
try {
|
||||
connection = ibmdb.openSync(`${dbConfig}${DBConstants.PROTOCOL}`);
|
||||
const result = connection.querySync(query);
|
||||
return { rows: result, rowsAffected: result.length };
|
||||
} catch (error) {
|
||||
throw new Error(`Error while executing query\n${error.message}`);
|
||||
} finally {
|
||||
if (connection) {
|
||||
try {
|
||||
connection.closeSync();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import moment from 'moment';
|
||||
|
||||
export default class DateUtil {
|
||||
/**
|
||||
* Generates date based on the input
|
||||
* @param format date format
|
||||
* @param days increment OR decrement the days
|
||||
* @param months increment OR decrement the months
|
||||
* @param years increment OR decrement the years
|
||||
* @returns
|
||||
*/
|
||||
public static dateGenerator(format: string, days: number, months: number, years: number): string {
|
||||
const date = moment().add(days, 'd').add(months, 'M').add(years, 'y')
|
||||
.format(format);
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customizes the date that has been given as input based on other input parameter
|
||||
* @param date to be customized
|
||||
* @param format date format
|
||||
* @param days increment OR decrement the days
|
||||
* @param months increment OR decrement the months
|
||||
* @param years increment OR decrement the years
|
||||
* @returns
|
||||
*/
|
||||
public static dateCustomizer(date: string, format: string, days: number, months: number, years: number): string {
|
||||
const customDate = moment(date, format).add(days, 'd').add(months, 'M').add(years, 'y')
|
||||
.format(format);
|
||||
return customDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates time in hr:min format based on the input
|
||||
* @param format time format
|
||||
* @param hours increment OR decrement the hours
|
||||
* @param minutes increment OR decrement the minutes
|
||||
* @returns
|
||||
*/
|
||||
public static timeGenerator(format: string, hours: number, minutes: number): string {
|
||||
const time = moment().add(minutes, 'm').add(hours, 'h').format(format);
|
||||
return time;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import excelToJson from "convert-excel-to-json";
|
||||
import ExcelConstants from "../constants/ExcelConstants";
|
||||
|
||||
export default class ExcelUtil {
|
||||
public static getTestDataArray(sheet: string) {
|
||||
const result = excelToJson({
|
||||
sourceFile: ExcelConstants.TEST_PATH,
|
||||
columnToKey: {
|
||||
'*': '{{columnHeader}}',
|
||||
},
|
||||
sheetStubs: true,
|
||||
header: { rows: 1 },
|
||||
sheets: [sheet],
|
||||
});
|
||||
return result[sheet];
|
||||
}
|
||||
|
||||
public static getSuiteTests(sheet: string) {
|
||||
const result = excelToJson({
|
||||
sourceFile: ExcelConstants.SUITE_PATH,
|
||||
columnToKey: {
|
||||
'*': '{{columnHeader}}',
|
||||
},
|
||||
sheetStubs: true,
|
||||
header: { rows: 1 },
|
||||
sheets: [sheet],
|
||||
});
|
||||
const testList: any[] = [];
|
||||
process.stdout.write("Creating Suite 0% ");
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const test of result[sheet]) {
|
||||
if (test.Run !== null && test.Run !== undefined && test.Run.toUpperCase() === ExcelConstants.YES) {
|
||||
testList.push({ TestName: test.TestName, Mode: test.Mode });
|
||||
}
|
||||
process.stdout.write("|");
|
||||
}
|
||||
if (testList.length === 0) {
|
||||
throw new Error(`${sheet} sheet does not have any test to run`);
|
||||
}
|
||||
process.stdout.write(" 100%");
|
||||
return testList;
|
||||
}
|
||||
|
||||
public static getTestData(sheet: string, testID: string) {
|
||||
const testData = this.getTestDataArray(sheet);
|
||||
let found = false;
|
||||
let data;
|
||||
for (let i = 0; i < testData.length; i++) {
|
||||
if (testData[i].TestID === testID) {
|
||||
data = testData[i];
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
throw new Error(`Test '${testID}' was not found on '${sheet}' sheet`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import fs from "fs";
|
||||
import pdfParse from "pdf-parse";
|
||||
|
||||
export default class PDFUtil {
|
||||
/**
|
||||
* Gets the text content of the pdf file
|
||||
* @param filePath File path
|
||||
* @returns PDF as text
|
||||
*/
|
||||
public static async getText(filePath: string): Promise<string> {
|
||||
const buffer = fs.readFileSync(filePath);
|
||||
try {
|
||||
const data = await pdfParse(buffer);
|
||||
return data.text;
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets number of pages in pdf file
|
||||
* @param filePath File path
|
||||
* @returns Number of pages
|
||||
*/
|
||||
public static async getNumberOfPages(filePath: string): Promise<number> {
|
||||
const buffer = fs.readFileSync(filePath);
|
||||
try {
|
||||
const data = await pdfParse(buffer);
|
||||
return data.numpages;
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the information about the pdf file
|
||||
* @param filePath File path
|
||||
* @returns PDF document info
|
||||
*/
|
||||
public static async getInfo(filePath: string): Promise<any> {
|
||||
const buffer = fs.readFileSync(filePath);
|
||||
try {
|
||||
const data = await pdfParse(buffer);
|
||||
return data.info;
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import randomString from "randomstring";
|
||||
import format from "string-format";
|
||||
|
||||
export default class StringUtil {
|
||||
/**
|
||||
* This method will return the formatted String by replacing value in {\d}
|
||||
* @param str : String to be formatted
|
||||
* @param replaceValue : value to replaced in formatted string
|
||||
* @returns str
|
||||
*/
|
||||
public static formatString(str: string, ...replaceValue: string[]): string {
|
||||
for (let i = 0; i < replaceValue.length; i++) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
str = str.split(`{${i}}`).join(replaceValue[i]);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the formatted String by replacing value in {key}
|
||||
* @param str : String to be formatted
|
||||
* @param replaceValue : value to replaced in formatted string
|
||||
* @returns str
|
||||
*/
|
||||
public static formatStringValue(str: string, replaceValue: any): string {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const [key, value] of Object.entries(replaceValue)) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
str = str.split(`{${key}}`).join(`${value}`);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces text in a string, using an string that supports replacement within a string.
|
||||
* @param str Original string
|
||||
* @param searchValue searches for and replace matches within the string.
|
||||
* @param replaceValue A string containing the text to replace for every successful match of searchValue in this string.
|
||||
* @returns
|
||||
*/
|
||||
public static replaceAll(str: string, searchValue: string, replaceValue: string): string {
|
||||
const replacer = new RegExp(searchValue, 'g');
|
||||
const replacedStr = str.replace(replacer, replaceValue);
|
||||
return replacedStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* replaces the regex with string value
|
||||
* @param str
|
||||
* @param regex
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
public static getRegXLocator(str: string, regex: RegExp, value: string) {
|
||||
return str.replace(regex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates random alphanumeric string of given length
|
||||
* @param length
|
||||
* @returns
|
||||
*/
|
||||
public static randomAlphanumericString(length: number): string {
|
||||
const str = randomString.generate(length);
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates random string of given length
|
||||
* @param length
|
||||
* @returns
|
||||
*/
|
||||
public static randomAlphabeticString(length: number): string {
|
||||
const str = randomString.generate({ length: length, charset: 'alphabetic' });
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates random string of given length with all letters a as uppercase
|
||||
* @param length
|
||||
* @returns
|
||||
*/
|
||||
public static randomUppercaseString(length: number): string {
|
||||
const str = randomString.generate({ length: length, charset: 'alphabetic', capitalization: "uppercase" });
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates random string of given length with all letters a as lowercase
|
||||
* @param length
|
||||
* @returns
|
||||
*/
|
||||
public static randomLowercaseString(length: number): string {
|
||||
const str = randomString.generate({ length: length, charset: 'alphabetic', capitalization: "lowercase" });
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates random number string of given length
|
||||
* @param length
|
||||
* @returns
|
||||
*/
|
||||
public static randomNumberString(length: number): string {
|
||||
const str = randomString.generate({ length: length, charset: 'numeric' });
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the formatted String by replacing value in {key} from Object
|
||||
* @param str
|
||||
* @param obj
|
||||
* @returns
|
||||
*/
|
||||
public static formatStringFromObject(str: string, obj: any): string {
|
||||
return format(str, obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const xpath = require('xpath');
|
||||
const Dom = require('xmldom').DOMParser;
|
||||
|
||||
export default class XMLParserUtil {
|
||||
/**
|
||||
* Get content of tag in XML using xpath
|
||||
* @param xPathExpression xpath for the tag
|
||||
* @param xml as string
|
||||
*/
|
||||
public static getTagContentByXpath(xml: string, xPathExpression: string): string {
|
||||
const doc = new Dom().parseFromString(xml);
|
||||
const text = xpath.select(`string(${xPathExpression})`, doc);
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value of attribute in XML using xpath
|
||||
* @param xPathExpression xpath for the attribute
|
||||
* @param xml as string
|
||||
*/
|
||||
public static getAttributeValueByXpath(xml: string, xPathExpression: string): string {
|
||||
const doc = new Dom().parseFromString(xml);
|
||||
const text = xpath.select1(xPathExpression, doc).value;
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"address": {
|
||||
"geolocation": {
|
||||
"lat": "-37.3159",
|
||||
"long": "81.1496"
|
||||
},
|
||||
"city": "Bangalore",
|
||||
"street": "new road",
|
||||
"number": 56431,
|
||||
"zipcode": "3874-34562"
|
||||
},
|
||||
"email": "{userName}@gmail.com",
|
||||
"username": "{userName}",
|
||||
"password": "{password}",
|
||||
"name": {
|
||||
"firstname": "john",
|
||||
"lastname": "doe"
|
||||
},
|
||||
"phone": "{phoneNumber}"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"username": "johnd",
|
||||
"password": "m38rmF$"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"address": {
|
||||
"geolocation": {
|
||||
"lat": "-37.3159",
|
||||
"long": "81.1496"
|
||||
},
|
||||
"city": "Bangalore",
|
||||
"street": "new road",
|
||||
"number": 56431,
|
||||
"zipcode": "3874-34562"
|
||||
},
|
||||
"email": "{userName}@gmail.com",
|
||||
"username": "{userName}",
|
||||
"password": "{password}",
|
||||
"name": {
|
||||
"firstname": "john",
|
||||
"lastname": "doe"
|
||||
},
|
||||
"phone": "{phoneNumber}"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<x:Envelope
|
||||
xmlns:x="http://schemas.xmlsoap.org/soap/envelope/"
|
||||
xmlns:com="com.advantage.online.store.accountservice">
|
||||
<x:Header/>
|
||||
<x:Body>
|
||||
<com:AccountCreateRequest>
|
||||
<com:lastName>Ashok</com:lastName>
|
||||
<com:firstName>Kumar</com:firstName>
|
||||
<com:loginName>{userName}</com:loginName>
|
||||
<com:countryId>India,in</com:countryId>
|
||||
<com:stateProvince>Assam</com:stateProvince>
|
||||
<com:cityName>Guwahati</com:cityName>
|
||||
<com:address>Odalbakra Street, Kahilipara</com:address>
|
||||
<com:zipcode>781034</com:zipcode>
|
||||
<com:phoneNumber>{phoneNumber}</com:phoneNumber>
|
||||
<com:email>{email}</com:email>
|
||||
<com:password>{password}</com:password>
|
||||
<com:accountType>USER</com:accountType>
|
||||
<com:allowOffersPromotion>true</com:allowOffersPromotion>
|
||||
<com:aobUser>false</com:aobUser>
|
||||
</com:AccountCreateRequest>
|
||||
</x:Body>
|
||||
</x:Envelope>
|
||||
@@ -0,0 +1,12 @@
|
||||
<x:Envelope
|
||||
xmlns:x="http://schemas.xmlsoap.org/soap/envelope/"
|
||||
xmlns:com="com.advantage.online.store.accountservice">
|
||||
<x:Header/>
|
||||
<x:Body>
|
||||
<com:AccountLoginRequest>
|
||||
<com:loginUser>{userName}</com:loginUser>
|
||||
<com:email>{email}</com:email>
|
||||
<com:loginPassword>{password}</com:loginPassword>
|
||||
</com:AccountLoginRequest>
|
||||
</x:Body>
|
||||
</x:Envelope>
|
||||
@@ -0,0 +1,11 @@
|
||||
<x:Envelope
|
||||
xmlns:x="http://schemas.xmlsoap.org/soap/envelope/"
|
||||
xmlns:com="com.advantage.online.store.accountservice">
|
||||
<x:Header/>
|
||||
<x:Body>
|
||||
<com:AccountLogoutRequest>
|
||||
<com:loginUser>{userId}</com:loginUser>
|
||||
<com:base64Token>{token}</com:base64Token>
|
||||
</com:AccountLogoutRequest>
|
||||
</x:Body>
|
||||
</x:Envelope>
|
||||
@@ -0,0 +1,11 @@
|
||||
<x:Envelope
|
||||
xmlns:x="http://schemas.xmlsoap.org/soap/envelope/"
|
||||
xmlns:com="com.advantage.online.store.accountservice">
|
||||
<x:Header/>
|
||||
<x:Body>
|
||||
<com:CountrySearchRequest>
|
||||
<com:internationalPhonePrefix></com:internationalPhonePrefix>
|
||||
<com:startOfName>{countryStartingWith}</com:startOfName>
|
||||
</com:CountrySearchRequest>
|
||||
</x:Body>
|
||||
</x:Envelope>
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
import ExcelUtil from "@utils/ExcelUtil";
|
||||
import { test } from "@base-test";
|
||||
import Allure from "@allure";
|
||||
import HomeSteps from "@uiSteps/HomeSteps";
|
||||
|
||||
const testData = ExcelUtil.getTestDataArray("ContactUsTest");
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const data of testData) {
|
||||
test(`${data.TestID} : ${data.Description}`, async ({ page }) => {
|
||||
Allure.attachDetails(data.Description, data.Issue);
|
||||
const home = new HomeSteps(page);
|
||||
await home.launchApplication();
|
||||
await home.enterContactUsDetails(data.Category, data.Product, data.Email, data.Subject);
|
||||
await home.sendMessage();
|
||||
await home.verifySuccessMessage(data.Message);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import HomeSteps from "@uiSteps/HomeSteps";
|
||||
import RegistrationSteps from "@uiSteps/RegistrationSteps";
|
||||
import { test } from "@base-test";
|
||||
import Allure from "@allure";
|
||||
import ExcelUtil from "@utils/ExcelUtil";
|
||||
|
||||
const sheet = "CreateAccountTest";
|
||||
const testData = ExcelUtil.getTestDataArray(sheet);
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const data of testData) {
|
||||
test(`${data.TestID} - ${data.Description}`, async ({ page }) => {
|
||||
Allure.attachDetails(data.Description, data.Issue);
|
||||
const home = new HomeSteps(page);
|
||||
await home.launchApplication();
|
||||
await home.navigateToCreateAccount();
|
||||
const register = new RegistrationSteps(page);
|
||||
const userName = await register.createAccount(data.Email, data.Password,
|
||||
data.ConfirmPassword, data.FirstName, data.LastName, data.PhoneNumber, data.Country,
|
||||
data.City, data.Address, data.State, data.PostalCode, data.AllowOffersPromotion);
|
||||
await register.saveRegistration();
|
||||
await home.validateLogin(userName);
|
||||
await home.logout();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import Allure from "@allure";
|
||||
import ExcelUtil from "@utils/ExcelUtil";
|
||||
import DatabaseStep from "@dbSteps/DatabaseStep";
|
||||
import { test } from "@base-test";
|
||||
|
||||
const testData = ExcelUtil.getTestDataArray("DatabaseTest");
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const data of testData) {
|
||||
test(`${data.TestID} - ${data.Description}`, async ({ }) => {
|
||||
Allure.attachDetails(data.Description, data.Issue);
|
||||
const db = new DatabaseStep();
|
||||
const result = await db.executeMSSQLQuery(data.Query);
|
||||
await db.verifyExecutionSuccess(result.rowsAffected[0]);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import ConfigurationSteps from "@uiSteps/ConfigurationSteps";
|
||||
import HomeSteps from "@uiSteps/HomeSteps";
|
||||
import { test } from "@base-test";
|
||||
import Allure from "@allure";
|
||||
|
||||
test(`DownloadTest - To download pdf file from application`, async ({ page }) => {
|
||||
Allure.attachDetails("To download pdf file from application", null);
|
||||
const home = new HomeSteps(page);
|
||||
await home.launchApplication();
|
||||
const newPage = await home.navigateToManagementConsole();
|
||||
await page.close();
|
||||
const configStep = new ConfigurationSteps(newPage);
|
||||
const fileName = await configStep.downloadAOSBackendPDF();
|
||||
await configStep.verifyPDFFilePageCount(fileName, 4);
|
||||
await configStep.verifyPDFFileText(fileName, "Advantage Online Shopping (AOS)");
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import HomeSteps from "@uiSteps/HomeSteps";
|
||||
import RegistrationSteps from "@uiSteps/RegistrationSteps";
|
||||
import { test } from "@base-test";
|
||||
import Allure from "@allure";
|
||||
import ExcelUtil from "@utils/ExcelUtil";
|
||||
|
||||
const SHEET = "LoginTest";
|
||||
let home: HomeSteps;
|
||||
test.beforeEach(async ({ page }) => {
|
||||
home = new HomeSteps(page);
|
||||
});
|
||||
|
||||
const data1 = ExcelUtil.getTestData(SHEET, "TC01_ValidLogin");
|
||||
test(`${data1.TestID} - ${data1.Description}`, async () => {
|
||||
Allure.attachDetails(data1.Description, data1.Issue);
|
||||
await home.launchApplication();
|
||||
await home.login(data1.UserName, data1.Password);
|
||||
await home.validateLogin(data1.UserName);
|
||||
await home.logout();
|
||||
});
|
||||
|
||||
const data2 = ExcelUtil.getTestData(SHEET, "TC02_InValidLogin");
|
||||
test(`${data2.TestID} - ${data2.Description}`, async () => {
|
||||
Allure.attachDetails(data2.Description, data2.Issue);
|
||||
await home.launchApplication();
|
||||
await home.login(data2.UserName, data2.Password);
|
||||
await home.validateInvalidLogin(data2.ErrorMessage);
|
||||
});
|
||||
|
||||
const data3 = ExcelUtil.getTestData(SHEET, "TC03_LoginCreateAccount");
|
||||
test(`${data3.TestID} - ${data3.Description}`, async ({ page }) => {
|
||||
Allure.attachDetails(data3.Description, data3.Issue);
|
||||
await home.launchApplication();
|
||||
await home.navigateToCreateAccount();
|
||||
const register = new RegistrationSteps(page);
|
||||
await register.alreadyHaveAccount();
|
||||
await home.enterLoginDetails(data3.UserName, data3.Password);
|
||||
await home.validateLogin(data3.UserName);
|
||||
await home.logout();
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import UserSteps from "@restSteps/UserSteps";
|
||||
import { test } from "@base-test";
|
||||
import Allure from "@allure";
|
||||
import ExcelUtil from "@utils/ExcelUtil";
|
||||
import StringUtil from "@utils/StringUtil";
|
||||
|
||||
const SHEET = "RESTUserTest";
|
||||
let user: UserSteps;
|
||||
test.beforeEach(async ({ page }) => {
|
||||
user = new UserSteps(page);
|
||||
});
|
||||
|
||||
const allUserData = ExcelUtil.getTestData(SHEET, "TC01_GetAllUserTest");
|
||||
test(`${allUserData.TestID} - ${allUserData.Description}`, async ({ gData }) => {
|
||||
Allure.attachDetails(allUserData.Description, allUserData.Issue);
|
||||
const response = await user.get(allUserData.EndPoint, allUserData.Operation);
|
||||
await user.verifyStatusCode(response, allUserData.Status);
|
||||
await user.verifyContentIsNotNull(response, allUserData.JSONPath, allUserData.Operation);
|
||||
const id = await user.extractResponseValue(response, allUserData.JSONPath, allUserData.Operation);
|
||||
gData.set("id", id);
|
||||
});
|
||||
|
||||
const singleUserData = ExcelUtil.getTestData(SHEET, "TC02_GetSingleUser");
|
||||
test(`${singleUserData.TestID} - ${singleUserData.Description}`, async ({ gData }) => {
|
||||
Allure.attachDetails(singleUserData.Description, singleUserData.Issue);
|
||||
const endPoint = StringUtil.formatStringValue(singleUserData.EndPoint, { ID: gData.get("id") });
|
||||
const response = await user.get(endPoint, singleUserData.Operation);
|
||||
await user.verifyStatusCode(response, singleUserData.Status);
|
||||
await user.verifyContent(response, singleUserData.JSONPath, gData.get("id"), singleUserData.Operation);
|
||||
});
|
||||
|
||||
const addUserData = ExcelUtil.getTestData(SHEET, "TC03_AddUser");
|
||||
test(`${addUserData.TestID} - ${addUserData.Description}`, async ({ gData }) => {
|
||||
Allure.attachDetails(addUserData.Description, addUserData.Issue);
|
||||
const userName = StringUtil.randomAlphabeticString(5);
|
||||
const password = StringUtil.randomAlphanumericString(5);
|
||||
const phoneNumber = StringUtil.randomNumberString(10);
|
||||
const requestData = {
|
||||
userName: userName,
|
||||
password: password,
|
||||
phoneNumber: phoneNumber,
|
||||
};
|
||||
const response = await user.post(addUserData.EndPoint, addUserData.RequestBody, requestData, addUserData.Operation);
|
||||
await user.verifyStatusCode(response, addUserData.Status);
|
||||
const id = await user.extractResponseValue(response, "$.id", addUserData.Operation);
|
||||
gData.set("id", id);
|
||||
gData.set("password", password);
|
||||
gData.set("userName", userName);
|
||||
});
|
||||
|
||||
const updateUserData = ExcelUtil.getTestData(SHEET, "TC04_UpdateUser");
|
||||
test(`${updateUserData.TestID} - ${updateUserData.Description}`, async ({ gData }) => {
|
||||
Allure.attachDetails(updateUserData.Description, updateUserData.Issue);
|
||||
const endPoint = StringUtil.formatStringValue(updateUserData.EndPoint, { ID: gData.get("id") });
|
||||
const phoneNumber = StringUtil.randomNumberString(10);
|
||||
const requestData = {
|
||||
userName: gData.get("userName"),
|
||||
password: gData.get("password"),
|
||||
phoneNumber: phoneNumber,
|
||||
};
|
||||
const response = await user.put(endPoint, updateUserData.RequestBody, requestData,
|
||||
updateUserData.Operation);
|
||||
await user.verifyStatusCode(response, updateUserData.Status);
|
||||
await user.verifyContent(response, "$.password", gData.get("password"), "Password");
|
||||
await user.verifyContent(response, "$.username", gData.get("userName"), "User Name");
|
||||
await user.verifyContent(response, updateUserData.JSONPath, phoneNumber, "Phone Number");
|
||||
});
|
||||
|
||||
const loginData = ExcelUtil.getTestData(SHEET, "TC05_StoreLogin");
|
||||
test(`${loginData.TestID} - ${loginData.Description}`, async ({ gData }) => {
|
||||
Allure.attachDetails(loginData.Description, loginData.Issue);
|
||||
const requestData = {
|
||||
userName: gData.get("userName"),
|
||||
password: gData.get("password"),
|
||||
};
|
||||
const response = await user.post(loginData.EndPoint, loginData.RequestBody, requestData, loginData.Operation);
|
||||
await user.verifyStatusCode(response, loginData.Status);
|
||||
await user.verifyContentIsNotNull(response, loginData.JSONPath, "Token");
|
||||
});
|
||||
|
||||
const deleteData = ExcelUtil.getTestData(SHEET, "TC06_DeleteUser");
|
||||
test(`${deleteData.TestID} - ${deleteData.Description}`, async ({ gData }) => {
|
||||
Allure.attachDetails(deleteData.Description, deleteData.Issue);
|
||||
const endPoint = StringUtil.formatStringValue(deleteData.EndPoint, { ID: gData.get("id") });
|
||||
const response = await user.delete(endPoint, deleteData.Operation);
|
||||
await user.verifyStatusCode(response, deleteData.Status);
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
import AccountServiceSteps from "@soapSteps/AccountServiceSteps";
|
||||
import { test } from "@base-test";
|
||||
import Allure from "@allure";
|
||||
import ExcelUtil from "@utils/ExcelUtil";
|
||||
import StringUtil from "@utils/StringUtil";
|
||||
|
||||
const SHEET = "SOAPAccountServiceTest";
|
||||
let account: AccountServiceSteps;
|
||||
test.beforeEach(async ({ page }) => {
|
||||
account = new AccountServiceSteps(page);
|
||||
});
|
||||
|
||||
const createData = ExcelUtil.getTestData(SHEET, "TC01_AccountCreateRequest");
|
||||
test(`${createData.TestID} - ${createData.Description}`, async ({ gData }) => {
|
||||
Allure.attachDetails(createData.Description, createData.Issue);
|
||||
const userName = StringUtil.randomAlphabeticString(5);
|
||||
const password = `${StringUtil.randomUppercaseString(1)}${StringUtil.randomLowercaseString(4)}${StringUtil.randomNumberString(3)}`;
|
||||
const phoneNumber = StringUtil.randomNumberString(10);
|
||||
const email = `${userName}@email.com`;
|
||||
const requestData = {
|
||||
userName: userName,
|
||||
password: password,
|
||||
email: email,
|
||||
phoneNumber: phoneNumber,
|
||||
};
|
||||
const response = await account.request(createData.EndPoint, createData.RequestBody,
|
||||
requestData, createData.Operation);
|
||||
await account.verifyResponse(response, createData.Xpath, createData.Result, createData.Operation);
|
||||
gData.set("userName", userName);
|
||||
gData.set("password", password);
|
||||
gData.set("email", email);
|
||||
});
|
||||
|
||||
const loginData = ExcelUtil.getTestData(SHEET, "TC02_AccountLoginRequest");
|
||||
test(`${loginData.TestID} - ${loginData.Description}`, async ({ gData }) => {
|
||||
Allure.attachDetails(loginData.Description, loginData.Issue);
|
||||
const requestData = {
|
||||
userName: gData.get("userName"),
|
||||
password: gData.get("password"),
|
||||
email: gData.get("email"),
|
||||
};
|
||||
const response = await account.request(loginData.EndPoint, loginData.RequestBody,
|
||||
requestData, loginData.Operation);
|
||||
await account.verifyResponse(response, loginData.Xpath, loginData.Result, loginData.Operation);
|
||||
gData.set("token", await account.getResponseContent(response, "//*[local-name()='token']", loginData.Operation));
|
||||
gData.set("userId", await account.getResponseContent(response, "//*[local-name()='userId']", loginData.Operation));
|
||||
});
|
||||
|
||||
const logoutData = ExcelUtil.getTestData(SHEET, "TC03_AccountLogoutRequest");
|
||||
test(`${logoutData.TestID} - ${logoutData.Description}`, async ({ gData }) => {
|
||||
Allure.attachDetails(logoutData.Description, logoutData.Issue);
|
||||
const requestData = {
|
||||
token: gData.get("token"),
|
||||
userId: gData.get("userId"),
|
||||
};
|
||||
const response = await account.request(logoutData.EndPoint, logoutData.RequestBody,
|
||||
requestData, logoutData.Operation);
|
||||
await account.verifyResponse(response, logoutData.Xpath, logoutData.Result, logoutData.Operation);
|
||||
});
|
||||
|
||||
const searchData = ExcelUtil.getTestData(SHEET, "TC04_CountrySearchRequest");
|
||||
test(`${searchData.TestID} - ${searchData.Description}`, async ({ }) => {
|
||||
Allure.attachDetails(searchData.Description, searchData.Issue);
|
||||
const response = await account.request(searchData.EndPoint, searchData.RequestBody,
|
||||
{ countryStartingWith: searchData.Result }, searchData.Operation);
|
||||
await account.verifyResponseContains(response, searchData.Xpath, searchData.Result, searchData.Operation);
|
||||
});
|
||||
@@ -0,0 +1,317 @@
|
||||
import { Browser, BrowserContext, Page, chromium } from 'playwright';
|
||||
|
||||
// Interfaces for type safety
|
||||
interface Credentials {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface AutomationConfig {
|
||||
website: string;
|
||||
credentials: Credentials;
|
||||
settings: {
|
||||
headless: boolean;
|
||||
slowMo: number;
|
||||
timeout: number;
|
||||
screenshotPath: string;
|
||||
errorScreenshotPath: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface TopicInfo {
|
||||
title: string;
|
||||
author: string;
|
||||
url: string;
|
||||
category: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface AutomationResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
topicInfo?: TopicInfo;
|
||||
screenshotPath?: string;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
class ISharkFlyAutomation {
|
||||
private browser: Browser | null = null;
|
||||
private context: BrowserContext | null = null;
|
||||
private page: Page | null = null;
|
||||
|
||||
constructor(private config: AutomationConfig) {}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
console.log('🚀 Initializing browser...');
|
||||
this.browser = await chromium.launch({
|
||||
headless: this.config.settings.headless,
|
||||
slowMo: this.config.settings.slowMo
|
||||
});
|
||||
|
||||
this.context = await this.browser.newContext({
|
||||
viewport: { width: 1920, height: 1080 },
|
||||
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||
});
|
||||
|
||||
this.page = await this.context.newPage();
|
||||
|
||||
// Set default timeout
|
||||
this.page.setDefaultTimeout(this.config.settings.timeout);
|
||||
|
||||
console.log('✅ Browser initialized successfully');
|
||||
}
|
||||
|
||||
async navigateToWebsite(): Promise<void> {
|
||||
if (!this.page) throw new Error('Page not initialized');
|
||||
|
||||
console.log(`🌐 Navigating to ${this.config.website}...`);
|
||||
await this.page.goto(this.config.website);
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
console.log('✅ Website loaded successfully');
|
||||
}
|
||||
|
||||
async performLogin(): Promise<void> {
|
||||
if (!this.page) throw new Error('Page not initialized');
|
||||
|
||||
console.log('🔐 Starting login process...');
|
||||
|
||||
// Click Log In button
|
||||
await this.page.getByRole('button', { name: 'Log In' }).click();
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
console.log('✅ Login page loaded');
|
||||
|
||||
// Fill credentials
|
||||
await this.page.getByRole('textbox', { name: 'Email / Username' }).fill(this.config.credentials.username);
|
||||
console.log('✅ Username entered');
|
||||
|
||||
await this.page.getByRole('textbox', { name: 'Password' }).fill(this.config.credentials.password);
|
||||
console.log('✅ Password entered');
|
||||
|
||||
// Submit login
|
||||
await this.page.getByRole('button', { name: 'Log In', exact: true }).click();
|
||||
|
||||
// Wait for successful login redirect
|
||||
await this.page.waitForURL('**/latest', { timeout: this.config.settings.timeout });
|
||||
console.log('✅ Login successful, redirected to main page');
|
||||
}
|
||||
|
||||
async navigateToFirstTopic(): Promise<void> {
|
||||
if (!this.page) throw new Error('Page not initialized');
|
||||
|
||||
console.log('📖 Navigating to first topic...');
|
||||
|
||||
// Click on the first topic
|
||||
await this.page.getByRole('link', { name: 'Claude ai desktop 集成' }).click();
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
|
||||
console.log('✅ Topic page loaded successfully');
|
||||
}
|
||||
|
||||
async extractTopicInfo(): Promise<TopicInfo> {
|
||||
if (!this.page) throw new Error('Page not initialized');
|
||||
|
||||
console.log('📊 Extracting topic information...');
|
||||
|
||||
const title = await this.page.locator('h1').first().textContent() || 'Unknown Title';
|
||||
const author = await this.page.locator('[href*="/u/"]').first().textContent() || 'Unknown Author';
|
||||
const url = this.page.url();
|
||||
|
||||
// Extract category
|
||||
const categoryElement = await this.page.locator('[href*="/c/"]').first();
|
||||
const category = await categoryElement.textContent() || 'Unknown Category';
|
||||
|
||||
// Extract tags
|
||||
const tagElements = await this.page.locator('[href*="/tag/"]').all();
|
||||
const tags: string[] = [];
|
||||
for (const tagElement of tagElements) {
|
||||
const tag = await tagElement.textContent();
|
||||
if (tag) tags.push(tag.trim());
|
||||
}
|
||||
|
||||
const topicInfo: TopicInfo = {
|
||||
title: title.trim(),
|
||||
author: author.trim(),
|
||||
url,
|
||||
category: category.trim(),
|
||||
tags
|
||||
};
|
||||
|
||||
console.log('✅ Topic information extracted');
|
||||
return topicInfo;
|
||||
}
|
||||
|
||||
async takeScreenshot(path: string = this.config.settings.screenshotPath): Promise<void> {
|
||||
if (!this.page) throw new Error('Page not initialized');
|
||||
|
||||
console.log(`📸 Taking screenshot: ${path}`);
|
||||
await this.page.screenshot({
|
||||
path,
|
||||
fullPage: true,
|
||||
type: 'png'
|
||||
});
|
||||
console.log('✅ Screenshot saved successfully');
|
||||
}
|
||||
|
||||
async cleanup(): Promise<void> {
|
||||
console.log('🧹 Cleaning up resources...');
|
||||
|
||||
if (this.context) {
|
||||
await this.context.close();
|
||||
}
|
||||
|
||||
if (this.browser) {
|
||||
await this.browser.close();
|
||||
}
|
||||
|
||||
console.log('✅ Cleanup completed');
|
||||
}
|
||||
|
||||
async run(): Promise<AutomationResult> {
|
||||
try {
|
||||
await this.initialize();
|
||||
await this.navigateToWebsite();
|
||||
await this.performLogin();
|
||||
await this.navigateToFirstTopic();
|
||||
|
||||
const topicInfo = await this.extractTopicInfo();
|
||||
await this.takeScreenshot();
|
||||
|
||||
console.log('\n📋 Automation Summary:');
|
||||
console.log(`Topic: ${topicInfo.title}`);
|
||||
console.log(`Author: ${topicInfo.author}`);
|
||||
console.log(`Category: ${topicInfo.category}`);
|
||||
console.log(`Tags: ${topicInfo.tags.join(', ')}`);
|
||||
console.log(`URL: ${topicInfo.url}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Automation completed successfully',
|
||||
topicInfo,
|
||||
screenshotPath: this.config.settings.screenshotPath
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error during automation:', error);
|
||||
|
||||
// Take error screenshot if page exists
|
||||
if (this.page) {
|
||||
try {
|
||||
await this.page.screenshot({
|
||||
path: this.config.settings.errorScreenshotPath,
|
||||
fullPage: true
|
||||
});
|
||||
console.log(`💾 Error screenshot saved: ${this.config.settings.errorScreenshotPath}`);
|
||||
} catch (screenshotError) {
|
||||
console.error('Failed to take error screenshot:', screenshotError);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: 'Automation failed',
|
||||
error: error as Error,
|
||||
screenshotPath: this.config.settings.errorScreenshotPath
|
||||
};
|
||||
|
||||
} finally {
|
||||
await this.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
class ForumUtils {
|
||||
static async extractPostContent(page: Page): Promise<string> {
|
||||
const postContent = await page.evaluate(() => {
|
||||
const postElement = document.querySelector('[class*="post-content"]') as HTMLElement;
|
||||
return postElement ? postElement.innerText : 'Content not found';
|
||||
});
|
||||
|
||||
return postContent;
|
||||
}
|
||||
|
||||
static async navigateToCategory(page: Page, categoryName: string): Promise<void> {
|
||||
await page.getByRole('link', { name: categoryName }).click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
static async searchTopic(page: Page, searchQuery: string): Promise<void> {
|
||||
await page.getByRole('button', { name: 'Search' }).click();
|
||||
await page.fill('[placeholder*="search"]', searchQuery);
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
static async createNewTopic(
|
||||
page: Page,
|
||||
title: string,
|
||||
content: string,
|
||||
category?: string
|
||||
): Promise<void> {
|
||||
await page.getByRole('button', { name: 'New Topic' }).click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Fill title
|
||||
await page.fill('[placeholder*="title"]', title);
|
||||
|
||||
// Select category if provided
|
||||
if (category) {
|
||||
await page.selectOption('[name="category"]', category);
|
||||
}
|
||||
|
||||
// Fill content
|
||||
await page.fill('[class*="editor"], [contenteditable="true"]', content);
|
||||
|
||||
// Submit
|
||||
await page.getByRole('button', { name: 'Create Topic' }).click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
}
|
||||
|
||||
// Configuration
|
||||
const defaultConfig: AutomationConfig = {
|
||||
website: 'https://www.isharkfly.com',
|
||||
credentials: {
|
||||
username: 'hex',
|
||||
password: '******'
|
||||
},
|
||||
settings: {
|
||||
headless: false,
|
||||
slowMo: 1000,
|
||||
timeout: 15000,
|
||||
screenshotPath: 'isharkfly-topic-page.png',
|
||||
errorScreenshotPath: 'error-screenshot.png'
|
||||
}
|
||||
};
|
||||
|
||||
// Main execution function
|
||||
async function main(): Promise<void> {
|
||||
const automation = new ISharkFlyAutomation(defaultConfig);
|
||||
const result = await automation.run();
|
||||
|
||||
if (result.success) {
|
||||
console.log('🎉 Automation completed successfully!');
|
||||
if (result.topicInfo) {
|
||||
console.log('📄 Topic Info:', result.topicInfo);
|
||||
}
|
||||
} else {
|
||||
console.error('💥 Automation failed:', result.error?.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use as module
|
||||
export {
|
||||
ISharkFlyAutomation,
|
||||
ForumUtils,
|
||||
AutomationConfig,
|
||||
AutomationResult,
|
||||
TopicInfo,
|
||||
Credentials,
|
||||
defaultConfig
|
||||
};
|
||||
|
||||
// Run if this file is executed directly
|
||||
if (require.main === module) {
|
||||
main().catch(console.error);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"status": "failed",
|
||||
"failedTests": [
|
||||
"798a22a2e3736b800263-977dc67fdc378d8186c4",
|
||||
"798a22a2e3736b800263-d114c2ca1ff6b7f6232d",
|
||||
"798a22a2e3736b800263-88c09e8370386df993c0"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
"@pages/*":["advantage/pages/*"],
|
||||
"@uiConstants/*":["advantage/constants/*"],
|
||||
"@restConstants/*":["API/REST/constants/*"],
|
||||
"@soapConstants/*":["API/SOAP/constants/*"],
|
||||
"@dbConstants/*":["database/constants/*"],
|
||||
"@asserts/*":["framework/playwright/asserts/*"],
|
||||
"@uiActions/*":["framework/playwright/actions/*"],
|
||||
"@apiActions/*":["framework/playwright/API/*"],
|
||||
"@utils/*":["framework/utils/*"],
|
||||
"@allure":["framework/reporter/Allure"],
|
||||
"@base-test":["framework/config/base-test"],
|
||||
"@dbSteps/*":["database/steps/*"],
|
||||
"@uiSteps/*":["advantage/steps/*"],
|
||||
"@restSteps/*":["API/REST/steps/*"],
|
||||
"@soapSteps/*":["API/SOAP/steps/*"],
|
||||
},
|
||||
"target": "ESNext",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "Node",
|
||||
"sourceMap": true,
|
||||
"outDir": "../tests-out",
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user