├── .nojekyll ├── gradle.properties ├── settings.gradle ├── script ├── startemulator.bat ├── plugins.txt ├── pixel.json ├── server.bat └── uploadapp.sh ├── docs ├── gif │ ├── gif_ios.gif │ └── gif_android.gif ├── img │ ├── appium1.png │ ├── appium2.png │ ├── appium3.png │ ├── appium4.png │ ├── uiautomator.png │ └── FrameworkArchitecture.png └── index.md ├── input ├── sqlite │ └── testdata.db └── Datasheet.csv ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .github ├── workflows │ ├── docs.yml │ ├── release-drafter.yml │ └── gradle.yml ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── feature_requested.md │ └── bug_report.md ├── config.yml └── PULL_REQUEST_TEMPLATE.md ├── src ├── main │ ├── resources │ │ ├── application.conf │ │ └── log4j.properties │ └── java │ │ └── com │ │ ├── core │ │ ├── Constants.java │ │ ├── DriverManager.java │ │ ├── Access.java │ │ ├── ServerManager.java │ │ ├── DataActions.java │ │ ├── ApiActions.java │ │ ├── ADB.java │ │ ├── AppiumController.java │ │ └── UserActions.java │ │ ├── exception │ │ ├── DeviceException.java │ │ ├── MobileAppException.java │ │ ├── AppiumException.java │ │ └── ElementException.java │ │ ├── model │ │ └── ApiResponse.java │ │ ├── logging │ │ ├── TestStatus.java │ │ └── ResultSender.java │ │ ├── constants │ │ └── Arg.java │ │ ├── reporting │ │ ├── Listeners │ │ │ ├── AnnotationTransformer.java │ │ │ ├── Retry.java │ │ │ └── TestListener.java │ │ ├── ExtentReports │ │ │ ├── ExtentManager.java │ │ │ └── ExtentTestManager.java │ │ └── AWS │ │ │ └── UploadReport.java │ │ ├── pages │ │ ├── LoginPage.java │ │ └── HomePage.java │ │ ├── config │ │ └── AppConfig.java │ │ └── utils │ │ └── FileSystem.java └── test │ ├── suite │ └── testng.xml │ └── java │ └── com │ ├── TestDefinitionLayer │ ├── TC_Test_SauseLabsApp.java │ └── TC_Test_AndroidBooking.java │ ├── MockServiceLayer │ ├── TestDatabase.java │ ├── WireMock.java │ └── TC001_MockService.java │ └── ServiceLayer │ └── API_Test.java ├── mkdocs.yml ├── docker-compose.yml ├── CONTRIBUTING.md ├── LICENSE ├── Jenkinsfile ├── Dockerfile ├── docker-compose-infra.yml ├── gradlew.bat ├── CODE_OF_CONDUCT.md ├── README.md └── gradlew /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'MobileTestFramework' 2 | 3 | -------------------------------------------------------------------------------- /script/startemulator.bat: -------------------------------------------------------------------------------- 1 | cd C:\Users\dipjyoti\AppData\Local\Android\Sdk\emulator 2 | emulator -avd Pixel3 -------------------------------------------------------------------------------- /docs/gif/gif_ios.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dipjyotimetia/MobileTestFramework/HEAD/docs/gif/gif_ios.gif -------------------------------------------------------------------------------- /docs/img/appium1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dipjyotimetia/MobileTestFramework/HEAD/docs/img/appium1.png -------------------------------------------------------------------------------- /docs/img/appium2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dipjyotimetia/MobileTestFramework/HEAD/docs/img/appium2.png -------------------------------------------------------------------------------- /docs/img/appium3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dipjyotimetia/MobileTestFramework/HEAD/docs/img/appium3.png -------------------------------------------------------------------------------- /docs/img/appium4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dipjyotimetia/MobileTestFramework/HEAD/docs/img/appium4.png -------------------------------------------------------------------------------- /docs/gif/gif_android.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dipjyotimetia/MobileTestFramework/HEAD/docs/gif/gif_android.gif -------------------------------------------------------------------------------- /docs/img/uiautomator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dipjyotimetia/MobileTestFramework/HEAD/docs/img/uiautomator.png -------------------------------------------------------------------------------- /input/sqlite/testdata.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dipjyotimetia/MobileTestFramework/HEAD/input/sqlite/testdata.db -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | .allure 4 | allure-results 5 | build 6 | out 7 | logs 8 | Reports 9 | ScreensDoc 10 | Screenshots -------------------------------------------------------------------------------- /docs/img/FrameworkArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dipjyotimetia/MobileTestFramework/HEAD/docs/img/FrameworkArchitecture.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dipjyotimetia/MobileTestFramework/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /script/plugins.txt: -------------------------------------------------------------------------------- 1 | git 2 | docker 3 | allure 4 | BrowserStack 5 | build-with-parameters 6 | configuration-as-code 7 | docker-plugin 8 | ssh-credentials -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Apr 14 13:45:26 AEST 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /input/Datasheet.csv: -------------------------------------------------------------------------------- 1 | TestcaseInstance,TestcaseName,Field1,Value1,Field2,Value2,Field3,Value3,Field4,Value4,Field5,Value5,Field6,Value6,Field7,Value7,Field8,Value8,Field9,Value9,Field10,Value10,Field11,Value11,Field12,Value12,Field13,Value13,Field14,Value14,Field15,Value15,Field16,Value16,Field17,Value17,Field18,Value18,Field19,Value19,Field20,Value20,Field21,Value21,Field22,Value22,Field23,Value23,Field24,Value24 2 | 1,TC_Test_AndroidBooking,UserName,,Password 3 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: actions/setup-python@v5 12 | with: 13 | python-version: 3.x 14 | - run: pip install mkdocs-material 15 | - run: mkdocs gh-deploy --force 16 | env: 17 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | application{ 2 | name = "MobileTestFramework" 3 | }, 4 | appium { 5 | appiumport = 4723 6 | proxyport = 9001 7 | }, 8 | api{ 9 | baseuri = "" 10 | username = "" 11 | userpass = "" 12 | tokenuri = "" 13 | clientuser = "" 14 | clisentpass = "" 15 | }, 16 | browserStack { 17 | appium_version = "1.22.0" 18 | debug = false 19 | devicelogs = false 20 | networkLogs = false 21 | acceptInsecureCerts = false 22 | appiumLogs = true 23 | video = true 24 | gpsLocation = "0,0" 25 | disableAnimations = false 26 | } 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | # Check for updates to GitHub Actions every weekday 7 | interval: "monthly" 8 | 9 | - package-ecosystem: "gradle" # See documentation for possible values 10 | directory: "/" # Location of package manifests 11 | schedule: 12 | interval: "monthly" 13 | time: "19:00" 14 | timezone: Australia/Melbourne 15 | rebase-strategy: "auto" 16 | labels: 17 | - "security" 18 | - "dependencies" 19 | open-pull-requests-limit: 10 20 | -------------------------------------------------------------------------------- /script/pixel.json: -------------------------------------------------------------------------------- 1 | { 2 | "capabilities": [ 3 | { 4 | "platformName": "Android", 5 | "applicationName": "UiAutomator2", 6 | "appPackage": "com.booking", 7 | "appActivity": ".startup.HomeActivity" 8 | } 9 | ], 10 | "configuration": { 11 | "cleanUpCycle": 2000, 12 | "timeout": 30000, 13 | "proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy", 14 | "url": "http://127.0.0.1:4000/wd/hub", 15 | "host": "127.0.0.1", 16 | "port": 4000, 17 | "maxSession": 1, 18 | "register": true, 19 | "registerCycle": 5000, 20 | "hubPort": "4444", 21 | "hubHost": "127.0.0.1" 22 | } 23 | } -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v6 15 | with: 16 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 17 | config-name: config.yml 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.RELEASE_DRAFTER_TOKEN }} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_requested.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: MobileTestFramework 2 | theme: 3 | name: material 4 | palette: 5 | primary: deep purple 6 | features: 7 | - search.suggest 8 | - search.highlight 9 | - navigation.tabs 10 | - navigation.tabs.sticky 11 | - navigation.indexes 12 | icon: 13 | repo: fontawesome/brands/git-alt 14 | plugins: 15 | - search 16 | extra: 17 | social: 18 | - icon: fontawesome/brands/github 19 | link: https://github.com/dipjyotimetia 20 | - icon: fontawesome/brands/linkedin 21 | link: https://linkedin.com/in/dipjyotimetia/ 22 | - icon: fontawesome/brands/medium 23 | link: https://medium.com/@dipjyotimetia 24 | copyright: Copyright © 2018 - 2021 Dipjyoti Metia 25 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | # more info https://hub.docker.com/r/appium/appium/ 4 | services: 5 | # Selenium hub 6 | selenium_hub: 7 | image: selenium/hub 8 | ports: 9 | - "4444:4444" 10 | 11 | # Appium docker android 12 | appium_android_device: 13 | image: appium/appium 14 | depends_on: 15 | - selenium_hub 16 | privileged: true 17 | volumes: 18 | - /dev/bus/usb:/dev/bus/usb 19 | - ~/.android:/root/.android 20 | - "./input/app:/root/tmp" 21 | environment: 22 | - CONNECT_TO_GRID=true 23 | - SELENIUM_HOST=selenium_hub #172.17.0.0 24 | - SELENIUM_PORT=4444 25 | - APPIUM_HOST="127.0.0.1" 26 | - APPIUM_PORT=4723 27 | - RELAXED_SECURITY=true -------------------------------------------------------------------------------- /script/server.bat: -------------------------------------------------------------------------------- 1 | https://www.browserstack.com/guide/selenium-grid-4-tutorial 2 | 3 | @REM Standalone 4 | java -jar selenium-server-4.0.0-beta-4.jar standalone 5 | 6 | @REM Hub and Node 7 | java -jar selenium-server-4.0.0-beta-4.jar hub 8 | 9 | java -jar selenium-server-4.0.0-alpha-7.jar node --detect-drivers true 10 | 11 | @REM Distributed 12 | java -jar selenium-server-4.0.0-beta-4.jar sessions 13 | 14 | java -jar selenium-server-4.0.0-beta-4.jar distributor --sessions http://localhost:5556 15 | 16 | java -jar selenium-server-4.0.0-beta-4.jar router --sessions http://localhost:5556 --distributor http://localhost:5553 17 | 18 | 19 | @REM # Window 2: the iOS node 20 | appium -p 4723 --nodeconfig /path/to/nodeconfig-ios.json 21 | 22 | @REM # Window 3: the Android node 23 | appium -p 4733 --nodeconfig /path/to/nodeconfig-android.json -------------------------------------------------------------------------------- /script/uploadapp.sh: -------------------------------------------------------------------------------- 1 | echo "**************** PUBLISH APP TO SAUCELABS WITH THIS DATA ******************" 2 | 3 | #-F "file=@/path/to/app/file/app-debug.ipa" 4 | echo "Enter the app name: " 5 | # shellcheck disable=SC2162 6 | read app 7 | echo "The Current User Name is $app" 8 | 9 | if [ "$app" == "android" ]; then 10 | curl -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \ 11 | -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ 12 | -F "url=https://github.com/saucelabs/sample-app-mobile/releases/download/2.7.1/Android.SauceLabs.Mobile.Sample.app.2.7.1.apk" 13 | else 14 | curl -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \ 15 | -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ 16 | -F "url=https://github.com/saucelabs/sample-app-mobile/releases/download/2.7.1/iOS.RealDevice.SauceLabs.Mobile.Sample.app.2.7.1.ipa" 17 | fi 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: 'Dipjyoti Metia' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Smartphone (please complete the following information):** 27 | - Device: [e.g. iPhone8] 28 | - OS: [e.g. iOS12.1] 29 | - Browser [e.g. stock browser, safari] 30 | - Version [e.g. 15] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Logging level 2 | solr.log=logs/ 3 | log4j.rootLogger=INFO, file, CONSOLE 4 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 5 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.CONSOLE.layout.ConversionPattern=%-5p - %d{yyyy-MM-dd HH:mm:ss.SSS}; %C: %m\n 7 | #- size rotation with log cleanup. 8 | log4j.appender.file=org.apache.log4j.RollingFileAppender 9 | log4j.appender.file.MaxFileSize=50MB 10 | log4j.appender.file.MaxBackupIndex=9 11 | #- File to log to and log format 12 | log4j.appender.file.File=${solr.log}/logfile.log 13 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 14 | log4j.appender.file.layout.ConversionPattern=%-5p - %d{yyyy-MM-dd HH:mm:ss.SSS}; %C{1}: %m\n 15 | log4j.logger.org.apache.zookeeper=WARN 16 | log4j.logger.org.apache.hadoop=WARN 17 | # set to INFO to enable infostream log messages 18 | log4j.logger.org.apache.solr.update.LoggingInfoStream=OFF -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to MobileTestFramework 2 | 3 | This framework welcomes contributions from everyone. There are a 4 | number of ways you can help: 5 | 6 | ## Bug Reports 7 | 8 | When opening new issues or commenting on existing issues please make 9 | sure discussions are related to concrete technical issues. 10 | 11 | It's imperative that issue reports outline the steps to reproduce 12 | the defect. If the issue can't be reproduced it will be closed. 13 | Please provide and describe what results you are seeing and what results you expect. 14 | 15 | ## Feature Requests 16 | 17 | If you find that some functions are missing, feel free to open an issue 18 | with details describing what feature(s) you'd like added or changed. 19 | 20 | If you'd like a hand at trying to implement the feature yourself, please feel free to create a pull request. 21 | 22 | ## Code Optimization 23 | 24 | You are always welcome to improve the code optimizations wherever required. 25 | -------------------------------------------------------------------------------- /src/test/suite/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/test/java/com/TestDefinitionLayer/TC_Test_SauseLabsApp.java: -------------------------------------------------------------------------------- 1 | package com.TestDefinitionLayer; 2 | 3 | import com.core.UserActions; 4 | import com.pages.LoginPage; 5 | import com.reporting.ExtentReports.ExtentTestManager; 6 | import io.qameta.allure.Feature; 7 | import io.qameta.allure.Link; 8 | import io.qameta.allure.Severity; 9 | import io.qameta.allure.SeverityLevel; 10 | import org.testng.annotations.Test; 11 | 12 | public class TC_Test_SauseLabsApp extends UserActions { 13 | @Link("Test") 14 | @Feature("test") 15 | @Severity(SeverityLevel.CRITICAL) 16 | @Test(description = "Demo Test") 17 | public void E2E_TestSauseLabs() { 18 | 19 | String TCname = "TC_Test_IOSSause"; 20 | 21 | LoginPage loginPage = new LoginPage(); 22 | 23 | ExtentTestManager.getTest().setDescription("Sause Login"); 24 | 25 | try { 26 | loginPage.login(); 27 | } catch (Exception e) { 28 | catchBlock(e); 29 | } finally { 30 | ExtentTestManager.endTest(); 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | categories: 4 | - title: '🚀 Features' 5 | labels: 6 | - 'feature' 7 | - 'enhancement' 8 | - title: '🐛 Bug Fixes' 9 | labels: 10 | - 'fix' 11 | - 'bugfix' 12 | - 'bug' 13 | - title: '✅ Tests' 14 | label: 'test' 15 | - title: '⚡ Performance' 16 | label: 'performance' 17 | - title: '📝 Documentation' 18 | label: 'docs' 19 | - title: '🔐 Security' 20 | label: 'security' 21 | - title: '🧰 Maintenance' 22 | label: 23 | - 'cicd' 24 | - 'tech' 25 | - title: '🤖 Dependencies' 26 | label: 'dependencies' 27 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 28 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 29 | version-resolver: 30 | major: 31 | labels: 32 | - 'major' 33 | minor: 34 | labels: 35 | - 'minor' 36 | patch: 37 | labels: 38 | - 'patch' 39 | default: patch 40 | template: | 41 | ## Changes 42 | 43 | $CHANGES -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Dipjyoti Metia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Summary 4 | 5 | 13 | 14 | ## Test Plan 15 | 16 | 17 | 18 | ### What's required for testing (prerequisites)? 19 | 20 | ### What are the steps to reproduce (after prerequisites)? 21 | 22 | ## Compatibility 23 | 24 | | Platform | Implemented | 25 | | -------- | :---------: | 26 | | Android | ✅❌ | 27 | | iOS | ✅❌ | 28 | 29 | ## Checklist 30 | 31 | 32 | 33 | - [ ] I have tested this on all mobile platforms 34 | - [ ] I added the documentation in `README.md` 35 | - [ ] I updated function description -------------------------------------------------------------------------------- /src/main/java/com/core/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.core; 25 | 26 | /** 27 | * @author Dipjyoti Metia 28 | */ 29 | public interface Constants { 30 | int KEYBOARD_ANIMATION_DELAY = 1000; 31 | } 32 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { label 'win-local' } 3 | stages { 4 | stage('Build') { 5 | steps { 6 | bat 'gradle clean' 7 | } 8 | } 9 | 10 | stage('SonarQube analysis') { 11 | steps { 12 | withSonarQubeEnv('My SonarQube Server') { 13 | bat 'gradle sonarqube' 14 | } 15 | } 16 | } 17 | 18 | stage('End to End Test'){ 19 | steps { 20 | bat 'gradle E2E -Pdownload=NO -Pcloud=YES' 21 | } 22 | } 23 | } 24 | 25 | post { 26 | always { 27 | bat 'gradle allureReport' 28 | script { 29 | allure([ 30 | includeProperties: false, 31 | jdk: '', 32 | properties: [], 33 | reportBuildPolicy: 'ALWAYS', 34 | results: [[path: 'build/allure-results']] 35 | ]) 36 | } 37 | publishHTML target: [ 38 | allowMissing: false, 39 | alwaysLinkToLastBuild: true, 40 | keepAll: false, 41 | reportDir: 'build/reports/tests/E2E', 42 | reportFiles: 'index.html', 43 | reportName: 'Gradle Report' 44 | ] 45 | 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/com/core/DriverManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.core; 25 | 26 | import io.appium.java_client.AppiumDriver; 27 | 28 | /** 29 | * @author Dipjyoti Metia 30 | */ 31 | public class DriverManager extends AppiumController { 32 | 33 | public AppiumDriver driver; 34 | 35 | public DriverManager() { 36 | this.driver = super.getDriver(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/exception/DeviceException.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.exception; 25 | 26 | /** 27 | * @author Dipjyoti Metia 28 | */ 29 | public class DeviceException extends Exception { 30 | /** 31 | * Device exception 32 | * 33 | * @param message exception message 34 | */ 35 | public DeviceException(String message) { 36 | super(message); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/exception/MobileAppException.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.exception; 25 | 26 | /** 27 | * @author Dipjyoti Metia 28 | */ 29 | public class MobileAppException extends Exception { 30 | /** 31 | * App exception 32 | * 33 | * @param message exception message 34 | */ 35 | public MobileAppException(String message) { 36 | super(message); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/exception/AppiumException.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.exception; 25 | 26 | /** 27 | * @author Dipjyoti Metia 28 | */ 29 | public class AppiumException extends Exception { 30 | 31 | /** 32 | * Appium server exception 33 | * 34 | * @param message exception message 35 | */ 36 | public AppiumException(String message) { 37 | super(message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:11-jdk 2 | 3 | ENV GRADLE_VERSION 6.9.0 4 | ENV ALLURE_VERSION 2.14.0 5 | 6 | RUN echo "deb [check-valid-until=no] http://cdn-fastly.deb.debian.org/debian jessie main" > /etc/apt/sources.list.d/jessie.list 7 | RUN echo "deb [check-valid-until=no] http://archive.debian.org/debian jessie-backports main" > /etc/apt/sources.list.d/jessie-backports.list 8 | RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list 9 | RUN apt-get -o Acquire::Check-Valid-Until=false update 10 | RUN apt-get install -y vim wget curl git python-pip python-dev build-essential 11 | 12 | # Install Gradle 13 | RUN wget https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip && \ 14 | unzip gradle-${GRADLE_VERSION}-bin.zip && \ 15 | mv gradle-${GRADLE_VERSION} /opt/ && \ 16 | rm gradle-${GRADLE_VERSION}-bin.zip 17 | ENV GRADLE_HOME /opt/gradle-${GRADLE_VERSION} 18 | ENV PATH $PATH:$GRADLE_HOME/bin 19 | 20 | RUN echo "export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64" >> ~/.bashrc 21 | 22 | ENV JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64/jre" 23 | ENV PATH $JAVA_HOME/bin:$PATH 24 | 25 | # Install allure commandline 26 | RUN curl -o allure-commandline-${ALLURE_VERSION}.tgz -Ls https://dl.bintray.com/qameta/maven/io/qameta/allure/allure-commandline/${ALLURE_VERSION}/allure-commandline-${ALLURE_VERSION}.tgz && \ 27 | tar -zxvf allure-commandline-${ALLURE_VERSION}.tgz -C /opt/ && ln -s /opt/allure-${ALLURE_VERSION}/bin/allure /usr/bin/allure && allure --version 28 | 29 | RUN groupadd -g 1000 jenkins && \ 30 | useradd -u 1000 -g 1000 -m -s /bin/bash jenkins 31 | 32 | USER jenkins -------------------------------------------------------------------------------- /src/main/java/com/exception/ElementException.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.exception; 25 | 26 | import org.openqa.selenium.ElementNotInteractableException; 27 | 28 | /** 29 | * @author Dipjyoti Metia 30 | */ 31 | public class ElementException extends ElementNotInteractableException { 32 | /** 33 | * Element exception 34 | * 35 | * @param message exception message 36 | */ 37 | public ElementException(String message) { 38 | super(message); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/model/ApiResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.model; 25 | 26 | import lombok.Getter; 27 | import lombok.ToString; 28 | 29 | /** 30 | * @author Dipjyoti Metia 31 | */ 32 | @Getter 33 | @ToString 34 | public class ApiResponse { 35 | private final int statusCode; 36 | private final String responseBody; 37 | 38 | public ApiResponse(int statusCode, String responseBody) { 39 | this.statusCode = statusCode; 40 | this.responseBody = responseBody; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/logging/TestStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.logging; 25 | 26 | import com.fasterxml.jackson.annotation.JsonProperty; 27 | import lombok.Setter; 28 | 29 | /** 30 | * @author Dipjyoti Metia 31 | */ 32 | @Setter 33 | public class TestStatus { 34 | 35 | @JsonProperty("testClass") 36 | private String testClass; 37 | 38 | @JsonProperty("description") 39 | private String description; 40 | 41 | @JsonProperty("status") 42 | private String status; 43 | 44 | @JsonProperty("executionTime") 45 | private String executionTime; 46 | } -------------------------------------------------------------------------------- /src/main/java/com/constants/Arg.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.constants; 25 | 26 | import io.appium.java_client.service.local.flags.ServerArgument; 27 | 28 | /** 29 | * @author Dipjyoti Metia 30 | */ 31 | public enum Arg implements ServerArgument { 32 | 33 | TIMEOUT("--command-timeout"), 34 | LOCAL_TIME_ZONE("--local-timezone"), 35 | LOG_LEVEL("--log-level"); 36 | 37 | private final String arg; 38 | 39 | Arg(String arg) { 40 | this.arg = arg; 41 | } 42 | 43 | @Override 44 | public String getArgument() { 45 | return arg; 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/com/reporting/Listeners/AnnotationTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.reporting.Listeners; 25 | 26 | import org.testng.IAnnotationTransformer; 27 | import org.testng.annotations.ITestAnnotation; 28 | 29 | import java.lang.reflect.Constructor; 30 | import java.lang.reflect.Method; 31 | 32 | /** 33 | * @author Dipjyoti Metia 34 | */ 35 | public class AnnotationTransformer implements IAnnotationTransformer { 36 | @Override 37 | public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) { 38 | annotation.setRetryAnalyzer(Retry.class); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docker-compose-infra.yml: -------------------------------------------------------------------------------- 1 | # Complete infra creation on the fly jenkins & allure report 2 | version: '3.8' 3 | services: 4 | allure: 5 | image: "frankescobar/allure-docker-service" 6 | environment: 7 | CHECK_RESULTS_EVERY_SECONDS: 1 8 | KEEP_HISTORY: 1 9 | ports: 10 | - "5050:5050" 11 | volumes: 12 | - "./build/allure-results:/app/allure-results" 13 | - "./build/allure-reports:/app/default-reports" 14 | 15 | allure-ui: 16 | image: "frankescobar/allure-docker-service-ui" 17 | environment: 18 | ALLURE_DOCKER_PUBLIC_API_URL: "http://localhost:5050" 19 | ALLURE_DOCKER_PUBLIC_API_URL_PREFIX: "" 20 | ports: 21 | - "5252:5252" 22 | 23 | #docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword 24 | jenkins_master: 25 | image: jenkins/jenkins:lts 26 | container_name: jenkins 27 | hostname: jenkins 28 | privileged: true 29 | user: root 30 | restart: always 31 | ports: 32 | - "8080:8080" 33 | - "50000:50000" 34 | volumes: 35 | - ~/jenkins-data:/var/jenkins_home 36 | - /var/run/docker.sock:/var/run/docker.sock 37 | - /usr/local/bin/docker:/usr/local/bin/docker 38 | - "./script/plugins.txt:/usr/share/jenkins/ref/plugins.txt" 39 | # - /var/jenkins_home:/usr/share/jenkins/ref/plugins 40 | # - /var/jenkins_home/plugins.txt:/usr/share/jenkins/ref/plugins.txt 41 | 42 | jenkins_slave: 43 | image: jenkins/jnlp-slave 44 | container_name: jnlp_slave 45 | user: root 46 | command: -url http://192.168.8.113:8080 47 | restart: always 48 | volumes: 49 | - ~/jenkins-data:/var/jenkins_home 50 | - /var/run/docker.sock:/var/run/docker.sock 51 | - /usr/local/bin/docker:/usr/local/bin/docker -------------------------------------------------------------------------------- /src/main/java/com/reporting/ExtentReports/ExtentManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.reporting.ExtentReports; 25 | 26 | import com.relevantcodes.extentreports.ExtentReports; 27 | 28 | /** 29 | * @author Dipjyoti Metia 30 | */ 31 | public class ExtentManager { 32 | private static ExtentReports extent; 33 | 34 | public synchronized static ExtentReports getReporter() { 35 | if (extent == null) { 36 | //Set HTML reporting file location 37 | String workingDir = System.getProperty("user.dir"); 38 | extent = new ExtentReports(workingDir + "\\Reports\\ExtentReportResults.html", true); 39 | } 40 | return extent; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/core/Access.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.core; 25 | 26 | import com.config.AppConfig; 27 | import com.typesafe.config.ConfigFactory; 28 | 29 | /** 30 | * @author Dipjyoti Metia 31 | */ 32 | public interface Access { 33 | 34 | AppConfig appConfig = new AppConfig(ConfigFactory.load()); 35 | 36 | int APPIUM_Port = appConfig.getAppiumPort(); 37 | int PROXY_Port = appConfig.getProxyPort(); 38 | String PIXEL = "emulator-5556"; 39 | String NEXUS = "emulator-5554"; 40 | String ANDROID_APP = ""; 41 | String AWS_KEY = ""; 42 | String AWS_SECRET = ""; 43 | String AWS_BUCKET = ""; 44 | String AWS_BUCKET_KEY = ""; 45 | String APK_PATH = ""; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-java@v4 15 | with: 16 | distribution: temurin 17 | java-version: 17 18 | - name: Grant execute permission for gradlew 19 | run: chmod +x gradlew 20 | - name: Upload app and set app location 21 | id: app-upload 22 | run: | 23 | IPA_UPLOAD_RESPONSE=$(curl -u "$USERNAME:$ACCESS" -X POST https://api-cloud.browserstack.com/app-automate/upload -F "url=https://github.com/saucelabs/sample-app-mobile/releases/download/2.7.1/iOS.RealDevice.SauceLabs.Mobile.Sample.app.2.7.1.ipa") 24 | APK_UPLOAD_RESPONSE=$(curl -u "$USERNAME:$ACCESS" -X POST https://api-cloud.browserstack.com/app-automate/upload -F "url=https://github.com/saucelabs/sample-app-mobile/releases/download/2.7.1/Android.SauceLabs.Mobile.Sample.app.2.7.1.apk") 25 | echo "::set-output name=IPA_URL::$(echo $IPA_UPLOAD_RESPONSE | jq -r '.app_url')" 26 | echo "::set-output name=APK_URL::$(echo $APK_UPLOAD_RESPONSE | jq -r '.app_url')" 27 | env: 28 | USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} 29 | ACCESS: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} 30 | - name: Gradle Run Test 31 | run: ./gradlew task e2e 32 | env: 33 | BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} 34 | BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} 35 | IPA_URL: ${{steps.app-upload.outputs.IPA_URL}} 36 | APK_URL: ${{steps.app-upload.outputs.APK_URL}} 37 | - name: Generate Report 38 | run: ./gradlew task allureReport 39 | - uses: actions/upload-artifact@v4 40 | with: 41 | name: allure-report 42 | path: build/reports/allure-report 43 | -------------------------------------------------------------------------------- /src/main/java/com/logging/ResultSender.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.logging; 25 | 26 | import com.fasterxml.jackson.databind.ObjectMapper; 27 | import kong.unirest.Unirest; 28 | 29 | /** 30 | * @author Dipjyoti Metia 31 | */ 32 | public class ResultSender { 33 | private static final ObjectMapper OM = new ObjectMapper(); 34 | private static final String CONTENT_TYPE = "Content-Type"; 35 | private static final String CONTENT_TYPE_VALUE = "application/json"; 36 | private static final String ELASTICSEARCH_URL = "http://localhost:9200/app/suite"; 37 | 38 | public static void send(final TestStatus testStatus) { 39 | try { 40 | Unirest.post(ELASTICSEARCH_URL) 41 | .header(CONTENT_TYPE, CONTENT_TYPE_VALUE) 42 | .body(OM.writeValueAsString(testStatus)).asJson(); 43 | System.out.println(OM.writeValueAsString(testStatus)); 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/reporting/ExtentReports/ExtentTestManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.reporting.ExtentReports; 25 | 26 | import com.relevantcodes.extentreports.ExtentReports; 27 | import com.relevantcodes.extentreports.ExtentTest; 28 | 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | 32 | /** 33 | * @author Dipjyoti Metia 34 | */ 35 | public class ExtentTestManager { 36 | static Map extentTestMap = new HashMap(); 37 | static ExtentReports extent = ExtentManager.getReporter(); 38 | 39 | public static synchronized ExtentTest getTest() { 40 | return (ExtentTest) extentTestMap.get((int) Thread.currentThread().getId()); 41 | } 42 | 43 | public static synchronized void endTest() { 44 | extent.endTest((ExtentTest) extentTestMap.get((int) Thread.currentThread().getId())); 45 | } 46 | 47 | public static synchronized ExtentTest startTest(String testName, String desc) { 48 | ExtentTest test = extent.startTest(testName, desc); 49 | extentTestMap.put((int) Thread.currentThread().getId(), test); 50 | return test; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/com/TestDefinitionLayer/TC_Test_AndroidBooking.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.TestDefinitionLayer; 25 | 26 | import com.core.UserActions; 27 | import com.pages.HomePage; 28 | import com.reporting.ExtentReports.ExtentTestManager; 29 | import io.qameta.allure.Feature; 30 | import io.qameta.allure.Link; 31 | import io.qameta.allure.Severity; 32 | import io.qameta.allure.SeverityLevel; 33 | import org.testng.annotations.Test; 34 | 35 | public class TC_Test_AndroidBooking extends UserActions { 36 | 37 | @Link("Test") 38 | @Feature("test") 39 | @Severity(SeverityLevel.CRITICAL) 40 | @Test(description = "Demo Test") 41 | public void E2E_TestAndroid_Bookings() { 42 | 43 | String TCname = "TC_Test_AndroidBooking"; 44 | 45 | HomePage homePage = new HomePage(); 46 | 47 | ExtentTestManager.getTest().setDescription("Search for bookings"); 48 | 49 | try { 50 | homePage.searchDestination() 51 | .selectDate() 52 | .search(); 53 | // CreateImageDoc(TCname); 54 | } catch (Exception e) { 55 | catchBlock(e); 56 | } finally { 57 | ExtentTestManager.endTest(); 58 | } 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/pages/LoginPage.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.pages; 25 | 26 | import com.core.Constants; 27 | import com.core.UserActions; 28 | import io.appium.java_client.pagefactory.*; 29 | import org.openqa.selenium.WebElement; 30 | import org.openqa.selenium.support.PageFactory; 31 | 32 | /** 33 | * @author Dipjyoti Metia 34 | */ 35 | public class LoginPage extends UserActions implements Constants { 36 | 37 | @iOSXCUITFindBys(value = {@iOSXCUITBy(accessibility = "test-Username"), @iOSXCUITBy(xpath = "//XCUIElementTypeTextField[@name=\"test-Username\"]")}) 38 | @AndroidFindBy(accessibility = "test-Username") 39 | private WebElement userName; 40 | 41 | @iOSXCUITFindBy(accessibility = "test-Password") 42 | @AndroidFindBy(accessibility = "test-Password") 43 | private WebElement password; 44 | 45 | @iOSXCUITFindBy(accessibility = "test-LOGIN") 46 | @AndroidFindBy(accessibility = "test-LOGIN") 47 | private WebElement loginButton; 48 | 49 | public LoginPage() { 50 | super(); 51 | PageFactory.initElements(new AppiumFieldDecorator(driver), this); 52 | } 53 | 54 | public void login() { 55 | enter(userName, "standard_user"); 56 | enter(password, "secret_sauce"); 57 | click(loginButton); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/MockServiceLayer/TestDatabase.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.MockServiceLayer; 25 | 26 | import org.testng.annotations.Test; 27 | 28 | import java.sql.*; 29 | 30 | public class TestDatabase { 31 | 32 | @Test 33 | public void TestNewDB() { 34 | ResultSet rs = null; 35 | Statement statement = null; 36 | Connection conn = null; 37 | try { 38 | String url = "jdbc:sqlite:input/sqlite/testdata.db"; 39 | conn = DriverManager.getConnection(url); 40 | if (conn != null) { 41 | statement = conn.createStatement(); 42 | String query = "select * from data"; 43 | rs = statement.executeQuery((query)); 44 | while (rs.next()) { 45 | System.out.println(rs.getString(2)); 46 | } 47 | } 48 | System.out.println("Connection to SQLite has been established."); 49 | 50 | } catch (SQLException e) { 51 | System.out.println(e.getMessage()); 52 | } finally { 53 | try { 54 | if (conn != null) { 55 | conn.close(); 56 | } 57 | } catch (SQLException ex) { 58 | System.out.println(ex.getMessage()); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/com/config/AppConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.config; 25 | 26 | import com.typesafe.config.Config; 27 | import lombok.Getter; 28 | 29 | /** 30 | * @author Dipjyoti Metia 31 | */ 32 | @Getter 33 | public class AppConfig { 34 | private final String applicationName; 35 | private final int appiumPort; 36 | private final int proxyPort; 37 | private final String appiumVersion; 38 | private final boolean debug; 39 | private final boolean deviceLogs; 40 | private final boolean networkLogs; 41 | private final boolean acceptInsecureCerts; 42 | private final boolean appiumLogs; 43 | private final boolean video; 44 | private final String gpsLocation; 45 | private final boolean disableAnimations; 46 | 47 | public AppConfig(Config config) { 48 | this.applicationName = config.getString("application.name"); 49 | this.appiumPort = config.getInt("appium.appiumport"); 50 | this.proxyPort = config.getInt("appium.proxyport"); 51 | this.appiumVersion = config.getString("browserStack.appium_version"); 52 | this.debug = config.getBoolean("browserStack.debug"); 53 | this.deviceLogs = config.getBoolean("browserStack.devicelogs"); 54 | this.networkLogs = config.getBoolean("browserStack.networkLogs"); 55 | this.acceptInsecureCerts = config.getBoolean("browserStack.acceptInsecureCerts"); 56 | this.appiumLogs = config.getBoolean("browserStack.appiumLogs"); 57 | this.video = config.getBoolean("browserStack.video"); 58 | this.gpsLocation = config.getString("browserStack.gpsLocation"); 59 | this.disableAnimations = config.getBoolean("browserStack.disableAnimations"); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/com/ServiceLayer/API_Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.ServiceLayer; 25 | 26 | import com.core.ApiActions; 27 | import com.reporting.ExtentReports.ExtentTestManager; 28 | import io.qameta.allure.Feature; 29 | import io.restassured.RestAssured; 30 | import io.restassured.response.Response; 31 | import org.json.simple.JSONObject; 32 | import org.testng.Assert; 33 | import org.testng.annotations.Test; 34 | 35 | public class API_Test extends ApiActions { 36 | 37 | @Feature("") 38 | @Test(description = "") 39 | @SuppressWarnings("unchecked") 40 | public void ServiceDemo() { 41 | ExtentTestManager.getTest().setDescription("Demo Service"); 42 | 43 | try { 44 | String DateTo = "2018-10-30T13:59:59Z"; 45 | String DateFrom = "2018-03-31T13:00:00Z"; 46 | 47 | RestAssured.baseURI = "Property.baseURI.getValue()"; 48 | 49 | JSONObject params = new JSONObject(); 50 | params.put("DateTo", DateTo); 51 | params.put("DateFrom", DateFrom); 52 | 53 | Response response = httpPost(params, "/Demo/Service"); 54 | Assert.assertEquals(getStatusCode(response) /*actual value*/, 200 /*expected value*/, "Correct status code returned"); 55 | // log.info("Response Body is => " + getBody(response)); 56 | T AcId = jsonPathEvaluator(response, "Results.ID"); 57 | T Demo = jsonPathEvaluator(response, "Results.Demo"); 58 | log("AcId: " + AcId); 59 | log("Demo: " + Demo); 60 | } catch (Exception e) { 61 | e.printStackTrace(); 62 | } finally { 63 | ExtentTestManager.endTest(); 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ## Mobile Test Framework 2 | 3 | ### Architecture 4 | ![Appium](img/FrameworkArchitecture.png) 5 | ### Supported Platforms 6 | 7 | Appium supports app automation across a variety of platforms, like iOS,Android, from any platform. Each 8 | platform is supported by one or more "drivers", which know how to automate that particular platform. Choose a driver 9 | below for specific information about how that driver works and how to set it up: 10 | 11 | * Android 12 | * The [UiAutomator2 Driver](https://appium.io/docs/en/drivers/android-uiautomator2/) 13 | * IOS 14 | * The [XCUITest](https://appium.io/docs/en/drivers/ios-xcuitest/) 15 | 16 | ### Why [Appium](https://appium.io/docs/en/about-appium/intro/)? 17 | 18 | * Appium is an Open source automation tool used for cross platform testing like native, hybrid and web applications on 19 | both the platforms IOS and Android. Its capability for testing all kinds of tools under one platform, makes it a 20 | multipurpose and convenient testing tool.Appium is called as a cross platform testing tool because it uses JSON wire 21 | protocol internally to interact with native apps of IOS and Android using Selenium Webdriver. 22 | 23 | ### Setup & Tools 24 | 25 | * Download and install [Nodejs](https://nodejs.org/en/download/) 26 | `` 27 | npm install -g appium 28 | `` 29 | `` 30 | npm install -g appium-doctor 31 | `` 32 | verify all appium dependencies 33 | * [Download Appium Desktop](https://github.com/appium/appium-desktop/releases) download the latest release 34 | * [Install InteliJ Community Edition](https://www.jetbrains.com/idea/download/) 35 | * [Java JDK_11](https://adoptopenjdk.net/) install jdk_11 version 36 | * [Gradle](https://gradle.org/next-steps/?version=6.7.1&format=bin) 37 | * [Allure](https://github.com/allure-framework/allure2/archive/2.14.0.zip) 38 | * Set the below environment variables 39 | 40 | ```shell 41 | * JAVA_HOME: Pointing to the Java SDK folder\bin 42 | * GRADLE_HOME: Pointing to Gradle directory\bin. 43 | * ALLURE_HOME: Pointing to allure directory\bin. 44 | * APPIUM_HOME: Pointing appium main.js from global location. 45 | * NODE_HOME: Pointing nodejs installation. 46 | ``` 47 | 48 | * For more details navigate to the above [Wiki Page](https://github.com/dipjyotimetia/MobileTestFramework/wiki) 49 | 50 | ### Connect - Local Devices: 51 | 52 | Connect an Android and an iOS Device using a USB cable to your PC 53 | 54 | - Follow documentation for device connection 55 | 56 | ### Getting Started 57 | 58 | ```shell 59 | $ git clone 60 | $ cd 61 | $ import project from intelij as a gradle project 62 | $ gradle clean 63 | $ gradle build 64 | $ gradle task E2E 65 | $ gradle allureReport 66 | $ gradle allureServe 67 | ``` 68 | 69 | ### Write your first user journey 70 | 71 | Create new class and name as the TC00*_E2E_TEST-*** 72 | 73 | - Provide jira link in @Link 74 | - Provide all the api components as @Feature 75 | - Provide test severity and description 76 | - Write test 77 | - Use CatchBlock in try/catch section 78 | 79 | #### Android Execution 80 | ![browserstack](gif/gif_android.gif) 81 | 82 | #### iOS Execution 83 | ![browserstack](gif/gif_ios.gif) 84 | 85 | -------------------------------------------------------------------------------- /src/main/java/com/reporting/AWS/UploadReport.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.reporting.AWS; 25 | 26 | import com.amazonaws.AmazonServiceException; 27 | import com.amazonaws.SdkClientException; 28 | import com.amazonaws.auth.AWSStaticCredentialsProvider; 29 | import com.amazonaws.auth.BasicAWSCredentials; 30 | import com.amazonaws.regions.Regions; 31 | import com.amazonaws.services.s3.AmazonS3; 32 | import com.amazonaws.services.s3.AmazonS3ClientBuilder; 33 | import com.amazonaws.services.s3.transfer.MultipleFileUpload; 34 | import com.amazonaws.services.s3.transfer.TransferManager; 35 | import com.amazonaws.services.s3.transfer.TransferManagerBuilder; 36 | import io.qameta.allure.Link; 37 | import lombok.extern.slf4j.Slf4j; 38 | import org.testng.annotations.Test; 39 | 40 | import java.io.File; 41 | 42 | /** 43 | * @author Dipjyoti Metia 44 | */ 45 | @Slf4j 46 | public class UploadReport { 47 | 48 | @Link("") 49 | @Test 50 | public void UploadToS3() throws Exception { 51 | String bucketName = ""; 52 | String keyName = "Reports"; 53 | String filePath = "build/reports/allure-report"; 54 | 55 | try { 56 | BasicAWSCredentials awsCreds = new BasicAWSCredentials("/", "/"); 57 | AmazonS3 s3Client = AmazonS3ClientBuilder.standard() 58 | .withRegion(Regions.AP_SOUTHEAST_2) 59 | .withCredentials(new AWSStaticCredentialsProvider(awsCreds)) 60 | .build(); 61 | s3Client.deleteObject(bucketName, keyName); 62 | 63 | TransferManager tm = TransferManagerBuilder.standard() 64 | .withS3Client(s3Client) 65 | .build(); 66 | 67 | MultipleFileUpload upload = tm.uploadDirectory(bucketName, keyName, new File(filePath), true); 68 | log.info("Object upload started"); 69 | 70 | upload.waitForCompletion(); 71 | log.info("Object upload complete"); 72 | } catch (AmazonServiceException e) { 73 | log.error("Amazon Service Exception", e); 74 | } catch (SdkClientException e) { 75 | log.error("SDK client Exception", e); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/reporting/Listeners/Retry.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.reporting.Listeners; 25 | 26 | import com.core.DriverManager; 27 | import com.relevantcodes.extentreports.LogStatus; 28 | import com.reporting.ExtentReports.ExtentTestManager; 29 | import org.openqa.selenium.OutputType; 30 | import org.openqa.selenium.TakesScreenshot; 31 | import org.testng.IRetryAnalyzer; 32 | import org.testng.ITestResult; 33 | 34 | /** 35 | * @author Dipjyoti Metia 36 | */ 37 | public class Retry extends DriverManager implements IRetryAnalyzer { 38 | private static final int maxTry = 0; //Run the failed test 2 times 39 | private int count = 0; 40 | 41 | /** 42 | * Retry Times 43 | * 44 | * @param iTestResult result 45 | * @return count 46 | */ 47 | @Override 48 | public boolean retry(ITestResult iTestResult) { 49 | if (!iTestResult.isSuccess()) { //Check if test not succeed 50 | if (count < maxTry) { //Check if maxtry count is reached 51 | count++; //Increase the maxTry count by 1 52 | iTestResult.setStatus(ITestResult.FAILURE); //Mark test as failed 53 | extendReportsFailOperations(iTestResult); //ExtentReports fail operations 54 | return true; //Tells TestNG to re-run the test 55 | } 56 | } else { 57 | iTestResult.setStatus(ITestResult.SUCCESS); //If test passes, TestNG marks it as passed 58 | } 59 | return false; 60 | } 61 | 62 | /** 63 | * Report Fail Operation 64 | * 65 | * @param iTestResult testResult 66 | */ 67 | public void extendReportsFailOperations(ITestResult iTestResult) { 68 | Object testClass = iTestResult.getInstance(); 69 | this.driver = ((DriverManager) testClass).getDriver(); 70 | String base64Screenshot = "data:image/png;base64," + ((TakesScreenshot) driver).getScreenshotAs(OutputType.BASE64); 71 | ExtentTestManager.getTest().log(LogStatus.FAIL, "Test Failed", 72 | ExtentTestManager.getTest().addBase64ScreenShot(base64Screenshot)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/pages/HomePage.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.pages; 25 | 26 | import com.core.Constants; 27 | import com.core.UserActions; 28 | import io.appium.java_client.pagefactory.AndroidFindBy; 29 | import io.appium.java_client.pagefactory.AppiumFieldDecorator; 30 | import io.appium.java_client.pagefactory.iOSXCUITBy; 31 | import org.openqa.selenium.By; 32 | import org.openqa.selenium.WebElement; 33 | import org.openqa.selenium.support.PageFactory; 34 | 35 | /** 36 | * @author Dipjyoti Metia 37 | */ 38 | public class HomePage extends UserActions implements Constants { 39 | 40 | @AndroidFindBy(id = "com.booking:id/search_details_text") 41 | @iOSXCUITBy(xpath = "") 42 | private WebElement destination; 43 | 44 | @AndroidFindBy(id = "com.booking:id/disam_search") 45 | private WebElement search; 46 | 47 | @AndroidFindBy(id = "com.booking:id/bt_accept") 48 | private WebElement acceptCookie; 49 | 50 | @AndroidFindBy(xpath = "//android.widget.ImageButton[@content-desc=\"Navigate up\"]") 51 | private WebElement closeButton; 52 | 53 | @AndroidFindBy(id = "com.booking:id/disambiguation_search_edittext") 54 | private WebElement searchEdit; 55 | 56 | @AndroidFindBy(id = "com.booking:id/button_positive") 57 | private WebElement gotIt; 58 | 59 | @AndroidFindBy(xpath = "(//android.widget.TextView[contains(@text,'Paris')])[1]") 60 | private WebElement select; 61 | 62 | @AndroidFindBy(id = "com.booking:id/calendar_confirm") 63 | private WebElement selectDate; 64 | 65 | @AndroidFindBy(id = "com.booking:id/search_search") 66 | private WebElement searchButton; 67 | 68 | public HomePage() { 69 | super(); 70 | PageFactory.initElements(new AppiumFieldDecorator(driver), this); 71 | } 72 | 73 | public HomePage searchDestination() { 74 | if (driver.findElements(By.id("com.booking:id/bt_accept")).size() != 0) { 75 | click(acceptCookie); 76 | click(closeButton); 77 | } 78 | waitForElement(destination); 79 | click(destination); 80 | enter(searchEdit, "Paris"); 81 | click(select); 82 | return this; 83 | } 84 | 85 | public HomePage selectDate() { 86 | click(selectDate); 87 | return this; 88 | } 89 | 90 | public void search() { 91 | click(searchButton); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at dipjyotimetia@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /src/main/java/com/core/ServerManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.core; 25 | 26 | import lombok.extern.slf4j.Slf4j; 27 | 28 | import java.io.*; 29 | import java.nio.charset.StandardCharsets; 30 | import java.util.Scanner; 31 | 32 | /** 33 | * @author Dipjyoti Metia 34 | */ 35 | @Slf4j 36 | public class ServerManager { 37 | 38 | private static String OS; 39 | private static String ANDROID_HOME; 40 | 41 | public static String getAndroidHome() throws Exception { 42 | if (ANDROID_HOME == null) { 43 | ANDROID_HOME = System.getenv("ANDROID_HOME"); 44 | if (ANDROID_HOME == null) 45 | throw new Exception("Failed to find ANDROID_HOME, make sure the environment variable is set"); 46 | } 47 | return ANDROID_HOME; 48 | } 49 | 50 | public static String getOS() { 51 | if (OS == null) OS = System.getenv("os.name"); 52 | return OS; 53 | } 54 | 55 | public static boolean isWindows() { 56 | return getOS().startsWith("Windows"); 57 | } 58 | 59 | public static boolean isMac() { 60 | return getOS().startsWith("Mac"); 61 | } 62 | 63 | public static String runCommand(String command) throws Exception { 64 | String output = null; 65 | try { 66 | Scanner scanner = new Scanner(Runtime.getRuntime().exec(command).getInputStream()).useDelimiter("\\A"); 67 | if (scanner.hasNext()) output = scanner.next(); 68 | } catch (IOException e) { 69 | throw new Exception(e.getMessage()); 70 | } 71 | return output; 72 | } 73 | 74 | public static String getWorkingDir() { 75 | return System.getProperty("user.dir"); 76 | } 77 | 78 | public static String read(File file) { 79 | StringBuilder output = new StringBuilder(); 80 | try { 81 | String line; 82 | FileReader fileReader = new FileReader(file); 83 | BufferedReader bufferedReader = new BufferedReader(fileReader); 84 | while ((line = bufferedReader.readLine()) != null) output.append(line).append("\n"); 85 | bufferedReader.close(); 86 | } catch (IOException error) { 87 | error.printStackTrace(); 88 | } 89 | return output.toString(); 90 | } 91 | 92 | public static void write(File file, String content) { 93 | try (Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { 94 | writer.write(content); 95 | writer.close(); 96 | } catch (IOException error) { 97 | error.printStackTrace(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Mobile Test Framework 2 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/8958948e2b0048a785417344e0dffe43)](https://www.codacy.com/gh/dipjyotimetia/MobileTestFramework/dashboard?utm_source=github.com&utm_medium=referral&utm_content=dipjyotimetia/MobileTestFramework&utm_campaign=Badge_Grade) 3 | ![CI](https://github.com/dipjyotimetia/MobileTestFramework/actions/workflows/gradle.yml/badge.svg) 4 | [![Testing Powered By SauceLabs](https://opensource.saucelabs.com/images/opensauce/powered-by-saucelabs-badge-white.png?sanitize=true "Testing Powered By SauceLabs")](https://saucelabs.com) 5 | ### Full-fledged Mobile, API and Database framework using appium and rest-assured 6 | 7 | 8 | 9 | ### Supported Platforms 10 | 11 | Appium supports app automation across a variety of platforms, like iOS,Android, from any platform. Each 12 | platform is supported by one or more "drivers", which know how to automate that particular platform. Choose a driver 13 | below for specific information about how that driver works and how to set it up: 14 | 15 | * Android 16 | * The [UiAutomator2 Driver](https://appium.io/docs/en/drivers/android-uiautomator2/) 17 | * IOS 18 | * The [XCUITest](https://appium.io/docs/en/drivers/ios-xcuitest/) 19 | 20 | ### Why [Appium](https://appium.io/docs/en/about-appium/intro/)? 21 | 22 | * Appium is an Open source automation tool used for cross platform testing like native, hybrid and web applications on 23 | both the platforms IOS and Android. Its capability for testing all kinds of tools under one platform, makes it a 24 | multipurpose and convenient testing tool.Appium is called as a cross platform testing tool because it uses JSON wire 25 | protocol internally to interact with native apps of IOS and Android using Selenium Webdriver. 26 | 27 | ### Setup & Tools 28 | 29 | * Download and install [Nodejs](https://nodejs.org/en/download/) 30 | `` 31 | npm install -g appium 32 | `` 33 | `` 34 | npm install -g appium-doctor 35 | `` 36 | verify all appium dependencies 37 | * [Download Appium Desktop](https://github.com/appium/appium-desktop/releases) download the latest release 38 | * [Install InteliJ Community Edition](https://www.jetbrains.com/idea/download/) 39 | * [Java JDK_11](https://adoptopenjdk.net/) install jdk_11 version 40 | * [Gradle](https://gradle.org/next-steps/?version=6.7.1&format=bin) 41 | * [Allure](https://github.com/allure-framework/allure2/archive/2.17.2.zip) 42 | * Set the below environment variables 43 | 44 | ```shell 45 | * JAVA_HOME: Pointing to the Java SDK folder\bin 46 | * GRADLE_HOME: Pointing to Gradle directory\bin. 47 | * ALLURE_HOME: Pointing to allure directory\bin. 48 | * APPIUM_HOME: Pointing appium main.js from global location. 49 | * NODE_HOME: Pointing nodejs installation. 50 | ``` 51 | 52 | * For more details navigate to the above [Wiki Page](https://github.com/dipjyotimetia/MobileTestFramework/wiki) 53 | 54 | ### Connect - Local Devices: 55 | 56 | Connect an Android and an iOS Device using a USB cable to your PC 57 | 58 | - Follow documentation for device connection 59 | 60 | ### Getting Started 61 | 62 | ```shell 63 | $ git clone 64 | $ cd 65 | $ import project from intelij as a gradle project 66 | $ gradle clean 67 | $ gradle build 68 | $ gradle task E2E 69 | $ gradle allureReport 70 | $ gradle allureServe 71 | ``` 72 | 73 | ### Write your first user journey 74 | 75 | Create new class and name as the TC00*_E2E_TEST-*** 76 | 77 | - Provide jira link in @Link 78 | - Provide all the api components as @Feature 79 | - Provide test severity and description 80 | - Write test 81 | - Use CatchBlock in try/catch section 82 | 83 | #### Android Execution 84 | ![browserstack](https://github.com/dipjyotimetia/MobileTestFramework/blob/master/docs/gif/gif_android.gif) 85 | 86 | #### iOS Execution 87 | ![browserstack](https://github.com/dipjyotimetia/MobileTestFramework/blob/master/docs/gif/gif_ios.gif) 88 | 89 | -------------------------------------------------------------------------------- /src/main/java/com/utils/FileSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.utils; 25 | 26 | import lombok.extern.slf4j.Slf4j; 27 | import org.apache.commons.io.FileUtils; 28 | import org.testng.Assert; 29 | 30 | import java.io.File; 31 | import java.io.IOException; 32 | import java.nio.charset.Charset; 33 | import java.nio.file.Files; 34 | import java.nio.file.Paths; 35 | 36 | /** 37 | * @author Dipjyoti Metia 38 | */ 39 | @Slf4j 40 | public class FileSystem { 41 | 42 | /** 43 | * Delete file path. 44 | * 45 | * @param path Path to file for folder. 46 | * @throws IOException When fail to delete it. 47 | */ 48 | public static void deletePath(String path) throws IOException { 49 | try { 50 | File file = new File(path); 51 | if (file.isDirectory()) { 52 | FileUtils.deleteDirectory(file); 53 | } else { 54 | file.delete(); 55 | } 56 | log.info("Delete " + path); 57 | } catch (Exception e) { 58 | String errorMessage = "Failed to delete " + path; 59 | log.error(errorMessage); 60 | throw new IOException(errorMessage); 61 | } 62 | } 63 | 64 | /** 65 | * Read content of file. 66 | * 67 | * @param filePath File path as String. 68 | * @return Content of file as String. 69 | * @throws IOException When fail to read file. 70 | */ 71 | public static String readFile(String filePath) throws IOException { 72 | byte[] encoded = Files.readAllBytes(Paths.get(filePath)); 73 | return new String(encoded, Charset.defaultCharset()); 74 | } 75 | 76 | /** 77 | * Check if path exists. 78 | * 79 | * @param path Path as String. 80 | * @return True if path exists. False if path does not exist. 81 | */ 82 | public static boolean exist(String path) { 83 | File file = new File(path); 84 | return file.exists(); 85 | } 86 | 87 | /** 88 | * Ensure path exists (create if does not exists). 89 | * 90 | * @param directory Path to directory. 91 | */ 92 | public static void ensureFolderExists(String directory) { 93 | File file = new File(directory); 94 | if (!file.exists()) { 95 | boolean result = file.mkdirs(); 96 | if (!result) { 97 | log.error("Failed to create folder: " + directory); 98 | } 99 | } 100 | } 101 | 102 | /** 103 | * Get size of file. 104 | * 105 | * @param path Path to file. 106 | * @return Size of file in kB. 107 | */ 108 | public static long getFileSize(String path) { 109 | File file; 110 | long size = 0; 111 | file = new File(path); 112 | if (file.exists()) { 113 | size = file.length() / 1024; // In KBs 114 | } else { 115 | Assert.fail("File '" + file + "' does not exist!"); 116 | } 117 | return size; 118 | } 119 | } -------------------------------------------------------------------------------- /src/main/java/com/reporting/Listeners/TestListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.reporting.Listeners; 25 | 26 | import com.core.DriverManager; 27 | import com.logging.ResultSender; 28 | import com.logging.TestStatus; 29 | import com.relevantcodes.extentreports.LogStatus; 30 | import com.reporting.ExtentReports.ExtentManager; 31 | import com.reporting.ExtentReports.ExtentTestManager; 32 | import lombok.extern.slf4j.Slf4j; 33 | import org.openqa.selenium.OutputType; 34 | import org.openqa.selenium.TakesScreenshot; 35 | import org.testng.ITestContext; 36 | import org.testng.ITestListener; 37 | import org.testng.ITestResult; 38 | 39 | /** 40 | * @author Dipjyoti Metia 41 | */ 42 | @Slf4j 43 | public class TestListener extends DriverManager implements ITestListener { 44 | 45 | private TestStatus testStatus; 46 | 47 | private static String getTestMethodName(ITestResult iTestResult) { 48 | return iTestResult.getMethod().getConstructorOrMethod().getName(); 49 | } 50 | 51 | @Override 52 | public void onStart(ITestContext iTestContext) { 53 | log.info("I am in onStart method " + iTestContext.getName()); 54 | iTestContext.setAttribute("WebDriver", this.driver); 55 | } 56 | 57 | @Override 58 | public void onFinish(ITestContext iTestContext) { 59 | log.info("I am in onFinish method " + iTestContext.getName()); 60 | ExtentTestManager.endTest(); 61 | ExtentManager.getReporter().flush(); 62 | } 63 | 64 | @Override 65 | public void onTestStart(ITestResult iTestResult) { 66 | this.testStatus = new TestStatus(); 67 | log.info("I am in onTestStart method " + getTestMethodName(iTestResult) + " start"); 68 | ExtentTestManager.startTest(iTestResult.getMethod().getMethodName(), ""); 69 | } 70 | 71 | @Override 72 | public void onTestSuccess(ITestResult iTestResult) { 73 | //this.sendStatus(iTestResult,"PASS"); 74 | log.info("I am in onTestSuccess method " + getTestMethodName(iTestResult) + " succeed"); 75 | ExtentTestManager.getTest().log(LogStatus.PASS, "Test passed"); 76 | } 77 | 78 | @Override 79 | public void onTestFailure(ITestResult iTestResult) { 80 | //this.sendStatus(iTestResult,"FAIL"); 81 | log.error("I am in onTestFailure method " + getTestMethodName(iTestResult) + " failed"); 82 | Object testClass = iTestResult.getInstance(); 83 | this.driver = ((DriverManager) testClass).getDriver(); 84 | String base64Screenshot = "data:image/png;base64," + ((TakesScreenshot) driver). 85 | getScreenshotAs(OutputType.BASE64); 86 | ExtentTestManager.getTest().log(LogStatus.FAIL, "Test Failed", 87 | ExtentTestManager.getTest().addBase64ScreenShot(base64Screenshot)); 88 | } 89 | 90 | @Override 91 | public void onTestSkipped(ITestResult iTestResult) { 92 | //this.sendStatus(iTestResult,"SKIP"); 93 | log.warn("I am in onTestSkipped method " + getTestMethodName(iTestResult) + " skipped"); 94 | ExtentTestManager.getTest().log(LogStatus.SKIP, "Test Skipped"); 95 | } 96 | 97 | @Override 98 | public void onTestFailedButWithinSuccessPercentage(ITestResult iTestResult) { 99 | log.error("Test failed but it is in defined success ratio " + getTestMethodName(iTestResult)); 100 | } 101 | 102 | private void sendStatus(ITestResult iTestResult, String status) { 103 | this.testStatus.setTestClass(iTestResult.getTestClass().getName()); 104 | this.testStatus.setDescription(iTestResult.getMethod().getDescription()); 105 | this.testStatus.setStatus(status); 106 | // this.testStatus.setExecutionDate(LocalDateTime.now().toString()); 107 | ResultSender.send(this.testStatus); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/com/MockServiceLayer/WireMock.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.MockServiceLayer; 25 | 26 | import com.github.tomakehurst.wiremock.http.Fault; 27 | import com.github.tomakehurst.wiremock.stubbing.Scenario; 28 | 29 | import static com.github.tomakehurst.wiremock.client.WireMock.*; 30 | 31 | 32 | public class WireMock { 33 | 34 | public WireMock() { 35 | } 36 | 37 | public void setupExampleStub() { 38 | 39 | stubFor(post(urlEqualTo("/pingpong")) 40 | .withRequestBody(matching("PING")) 41 | .willReturn(aResponse() 42 | .withStatus(200) 43 | .withHeader("Content-Type", "application/xml") 44 | .withBody("PONG"))); 45 | } 46 | 47 | public void setupStubURLMatching() { 48 | 49 | stubFor(get(urlEqualTo("/urlmatching")) 50 | .willReturn(aResponse() 51 | .withBody("URL matching") 52 | )); 53 | } 54 | 55 | public void setupStubRequestBodyMatching() { 56 | 57 | stubFor(post(urlEqualTo("/requestbodymatching")) 58 | .withRequestBody(containing("RequestBody")) 59 | .willReturn(aResponse() 60 | .withBody("Request body matching") 61 | )); 62 | } 63 | 64 | public void setupStubHeaderMatching() { 65 | 66 | stubFor(get(urlEqualTo("/headermatching")) 67 | .withHeader("Content-Type", containing("application/json")) 68 | .withHeader("DoesntExist", absent()) 69 | .willReturn(aResponse() 70 | .withBody("Header matching") 71 | )); 72 | } 73 | 74 | public void setupStubAuthorizationMatching() { 75 | 76 | stubFor(get(urlEqualTo("/authorizationmatching")) 77 | .withBasicAuth("username", "password") 78 | .willReturn(aResponse() 79 | .withBody("Authorization matching") 80 | )); 81 | } 82 | 83 | public void setupStubReturningErrorCode() { 84 | 85 | stubFor(get(urlEqualTo("/errorcode")) 86 | .willReturn(aResponse() 87 | .withStatus(500) 88 | )); 89 | } 90 | 91 | public void setupStubFixedDelay() { 92 | 93 | stubFor(get(urlEqualTo("/fixeddelay")) 94 | .willReturn(aResponse() 95 | .withFixedDelay(2000) 96 | )); 97 | } 98 | 99 | public void setupStubBadResponse() { 100 | 101 | stubFor(get(urlEqualTo("/badresponse")) 102 | .willReturn(aResponse() 103 | .withFault(Fault.MALFORMED_RESPONSE_CHUNK) 104 | )); 105 | } 106 | 107 | public void setupStubStateful() { 108 | 109 | stubFor(get(urlEqualTo("/order")).inScenario("Order processing") 110 | .whenScenarioStateIs(Scenario.STARTED) 111 | .willReturn(aResponse() 112 | .withBody("Your shopping cart is empty") 113 | )); 114 | 115 | stubFor(post(urlEqualTo("/order")).inScenario("Order processing") 116 | .whenScenarioStateIs(Scenario.STARTED) 117 | .withRequestBody(equalTo("Ordering 1 item")) 118 | .willReturn(aResponse() 119 | .withBody("Item placed in shopping cart") 120 | ) 121 | .willSetStateTo("ORDER_PLACED")); 122 | 123 | stubFor(get(urlEqualTo("/order")).inScenario("Order processing") 124 | .whenScenarioStateIs("ORDER_PLACED") 125 | .willReturn(aResponse() 126 | .withBody("There is 1 item in your shopping cart") 127 | )); 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/test/java/com/MockServiceLayer/TC001_MockService.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.MockServiceLayer; 25 | 26 | import com.github.tomakehurst.wiremock.WireMockServer; 27 | import com.github.tomakehurst.wiremock.core.WireMockConfiguration; 28 | import io.restassured.RestAssured; 29 | import org.hamcrest.Matchers; 30 | import org.testng.annotations.Test; 31 | 32 | import java.util.concurrent.TimeUnit; 33 | 34 | public class TC001_MockService { 35 | private final WireMock wme = new WireMock(); 36 | 37 | private final WireMockServer server = new WireMockServer(WireMockConfiguration.wireMockConfig().port(9876)); 38 | 39 | @Test 40 | public void testPingPongPositive() { 41 | 42 | server.start(); 43 | 44 | wme.setupExampleStub(); 45 | 46 | RestAssured.given(). 47 | body("PING"). 48 | when(). 49 | post("http://localhost:9876/pingpong"). 50 | then(). 51 | log(). 52 | body(). 53 | and(). 54 | assertThat(). 55 | statusCode(200). 56 | and(). 57 | body("output", Matchers.equalTo("PONG")); 58 | } 59 | 60 | @Test 61 | public void testURLMatching() { 62 | 63 | wme.setupStubURLMatching(); 64 | 65 | RestAssured.given(). 66 | when(). 67 | get("http://localhost:9876/urlmatching"). 68 | then(). 69 | assertThat(). 70 | body(Matchers.equalTo("URL matching")); 71 | } 72 | 73 | @Test 74 | public void testRequestBodyMatching() { 75 | 76 | wme.setupStubRequestBodyMatching(); 77 | 78 | RestAssured.given(). 79 | body("TestRequestBodyMatching"). 80 | when(). 81 | post("http://localhost:9876/requestbodymatching"). 82 | then(). 83 | assertThat(). 84 | body(Matchers.equalTo("Request body matching")); 85 | } 86 | 87 | @Test 88 | public void testHeaderMatching() { 89 | 90 | wme.setupStubHeaderMatching(); 91 | 92 | RestAssured.given(). 93 | contentType("application/json"). 94 | when(). 95 | get("http://localhost:9876/headermatching"). 96 | then(). 97 | assertThat(). 98 | body(Matchers.equalTo("Header matching")); 99 | } 100 | 101 | @Test 102 | public void testAuthorizationMatching() { 103 | 104 | wme.setupStubAuthorizationMatching(); 105 | 106 | RestAssured.given(). 107 | auth(). 108 | preemptive(). 109 | basic("username", "password"). 110 | when(). 111 | get("http://localhost:9876/authorizationmatching"). 112 | then(). 113 | assertThat(). 114 | body(Matchers.equalTo("Authorization matching")); 115 | } 116 | 117 | @Test 118 | public void testErrorCode() { 119 | 120 | wme.setupStubReturningErrorCode(); 121 | 122 | RestAssured.given(). 123 | when(). 124 | get("http://localhost:9876/errorcode"). 125 | then(). 126 | assertThat(). 127 | statusCode(500); 128 | } 129 | 130 | @Test 131 | public void testFixedDelay() { 132 | 133 | wme.setupStubFixedDelay(); 134 | 135 | RestAssured.given(). 136 | when(). 137 | get("http://localhost:9876/fixeddelay"). 138 | then(). 139 | assertThat(). 140 | time(Matchers.greaterThan(2000L), TimeUnit.MILLISECONDS); 141 | 142 | } 143 | 144 | @Test 145 | public void testStatefulStub() { 146 | 147 | wme.setupStubStateful(); 148 | 149 | RestAssured.given(). 150 | when(). 151 | get("http://localhost:9876/order"). 152 | then(). 153 | assertThat(). 154 | body(Matchers.equalTo("Your shopping cart is empty")); 155 | 156 | RestAssured.given(). 157 | body("Ordering 1 item"). 158 | when(). 159 | post("http://localhost:9876/order"). 160 | then(). 161 | assertThat(). 162 | body(Matchers.equalTo("Item placed in shopping cart")); 163 | 164 | RestAssured.given(). 165 | when(). 166 | get("http://localhost:9876/order"). 167 | then(). 168 | assertThat(). 169 | body(Matchers.equalTo("There is 1 item in your shopping cart")); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/main/java/com/core/DataActions.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.core; 25 | 26 | import lombok.extern.slf4j.Slf4j; 27 | 28 | import java.sql.DriverManager; 29 | import java.sql.*; 30 | 31 | /** 32 | * @author Dipjyoti Metia 33 | */ 34 | @Slf4j 35 | public class DataActions extends ApiActions { 36 | 37 | private static final String JDBC_URL = "jdbc:sqlserver://databaseserver;databaseName=Database;integratedSecurity=true"; 38 | 39 | /** 40 | * Get DB connection 41 | */ 42 | private void getDbConnection() { 43 | ResultSet rs = null; 44 | Connection connObj = null; 45 | Statement statement = null; 46 | try { 47 | Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); 48 | connObj = DriverManager.getConnection(JDBC_URL); 49 | if (connObj != null) { 50 | statement = connObj.createStatement(); 51 | String queryString = "select TOP 10 * from ;"; 52 | rs = statement.executeQuery(queryString); 53 | while (rs.next()) { 54 | log.info(rs.getString(2)); 55 | } 56 | } 57 | } catch (Exception sqlException) { 58 | log.error(sqlException.getMessage()); 59 | } finally { 60 | finallyBlock(rs, connObj, statement); 61 | } 62 | } 63 | 64 | /** 65 | * Execute query 66 | * 67 | * @param query query 68 | * @return result 69 | */ 70 | private String ExecuteQuery(String query) { 71 | String resultValue = ""; 72 | String columnName = ""; 73 | try { 74 | Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); 75 | } catch (ClassNotFoundException e) { 76 | log.error(e.getMessage()); 77 | } 78 | try (Connection connection = java.sql.DriverManager.getConnection(JDBC_URL)) { 79 | try (Statement stmt = connection.createStatement()) { 80 | try (ResultSet rs = stmt.executeQuery(query)) { 81 | while (rs.next()) { 82 | ResultSetMetaData rsmd = rs.getMetaData(); 83 | int columnCount = rsmd.getColumnCount(); 84 | for (int i = 1; i <= columnCount; i++) { 85 | try { 86 | rs.getString(i); 87 | } catch (NullPointerException e) { 88 | resultValue = "NULL"; 89 | log.info("column name:" + columnName + "|" + "Column value:" + resultValue); 90 | continue; 91 | } 92 | columnName = rsmd.getColumnName(i); 93 | resultValue = rs.getString(i); 94 | log.info("column name:" + columnName + "|" + "Column value:" + resultValue); 95 | } 96 | } 97 | } 98 | connection.close(); 99 | } catch (SQLException sq) { 100 | log.error(sq.getMessage()); 101 | } 102 | } catch (SQLException sq) { 103 | log.error(sq.getMessage()); 104 | } 105 | return resultValue; 106 | } 107 | 108 | private void displayRow(String title, ResultSet rs) { 109 | try { 110 | while (rs.next()) { 111 | log.info(rs.getString(title)); 112 | } 113 | } catch (Exception e) { 114 | log.error(e.getMessage()); 115 | } 116 | } 117 | 118 | private void finallyBlock(ResultSet rs, Connection connObj, Statement stmt) { 119 | if (rs != null) try { 120 | rs.close(); 121 | } catch (Exception e) { 122 | log.error(e.getMessage()); 123 | } 124 | if (connObj != null) try { 125 | connObj.close(); 126 | } catch (Exception e) { 127 | log.error(e.getMessage()); 128 | } 129 | if (stmt != null) try { 130 | stmt.close(); 131 | } catch (Exception e) { 132 | log.error(e.getMessage()); 133 | } 134 | } 135 | 136 | /** 137 | * Query executer 138 | * 139 | * @param query query 140 | * @return resultset 141 | */ 142 | private ResultSet query(String query) { 143 | Connection connObj = null; 144 | try { 145 | Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); 146 | connObj = DriverManager.getConnection(JDBC_URL); 147 | if (connObj != null) { 148 | Statement statement = connObj.createStatement(); 149 | ResultSet rs = statement.executeQuery(query); 150 | return rs; 151 | } 152 | } catch (Exception sqlException) { 153 | log.error(sqlException.getMessage()); 154 | } 155 | return null; 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/com/core/ApiActions.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.core; 25 | 26 | import com.github.javafaker.Faker; 27 | import io.qameta.allure.Step; 28 | import io.restassured.RestAssured; 29 | import io.restassured.http.ContentType; 30 | import io.restassured.http.Method; 31 | import io.restassured.module.jsv.JsonSchemaValidator; 32 | import io.restassured.response.Response; 33 | import io.restassured.response.ResponseBody; 34 | import io.restassured.response.ValidatableResponse; 35 | import io.restassured.specification.RequestSpecification; 36 | import lombok.extern.slf4j.Slf4j; 37 | import org.apache.http.HttpResponse; 38 | import org.apache.http.auth.AuthScope; 39 | import org.apache.http.auth.NTCredentials; 40 | import org.apache.http.client.CredentialsProvider; 41 | import org.apache.http.client.methods.HttpGet; 42 | import org.apache.http.impl.client.BasicCredentialsProvider; 43 | import org.apache.http.impl.client.CloseableHttpClient; 44 | import org.apache.http.impl.client.HttpClientBuilder; 45 | import org.json.simple.JSONObject; 46 | 47 | import java.io.BufferedReader; 48 | import java.io.InputStreamReader; 49 | import java.text.DateFormat; 50 | import java.text.SimpleDateFormat; 51 | import java.util.Date; 52 | 53 | /** 54 | * @author Dipjyoti Metia 55 | */ 56 | @Slf4j 57 | public class ApiActions { 58 | 59 | protected static final Faker faker = new Faker(); 60 | 61 | private final static String workstation = System.getenv("COMPUTERNAME"); 62 | private final static String domain = System.getenv("USERDOMAIN"); 63 | private final static String baseURI = ""; 64 | private final static String userName = ""; 65 | private final static String userPass = ""; 66 | private final static String tokenURI = ""; 67 | private final static String clientUser = ""; 68 | private final static String clientPass = ""; 69 | 70 | @SuppressWarnings("unchecked") 71 | private String authLogin() { 72 | RestAssured.baseURI = baseURI; 73 | RequestSpecification httpRequest = RestAssured 74 | .given() 75 | .contentType(ContentType.JSON); 76 | 77 | JSONObject requestParams = new JSONObject(); 78 | requestParams.put("Identifier", userName); 79 | requestParams.put("Password", userPass); 80 | 81 | Response response = httpRequest.body(requestParams.toJSONString()). 82 | request(Method.POST, "/Test"); 83 | 84 | return getHeaders(response, "auth"); 85 | } 86 | 87 | /** 88 | * Authentication token generate 89 | * 90 | * @return auth token 91 | */ 92 | protected String authToken() { 93 | String token = ""; 94 | CredentialsProvider credProvider = new BasicCredentialsProvider(); 95 | credProvider.setCredentials(AuthScope.ANY, 96 | new NTCredentials(clientUser, clientPass, workstation, domain)); 97 | try (CloseableHttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(credProvider).build()) { 98 | HttpGet request = new HttpGet(tokenURI); 99 | HttpResponse response = client.execute(request); 100 | BufferedReader bufReader = new BufferedReader(new InputStreamReader( 101 | response.getEntity().getContent())); 102 | StringBuilder builder = new StringBuilder(); 103 | String line; 104 | while ((line = bufReader.readLine()) != null) { 105 | builder.append(line); 106 | builder.append(System.lineSeparator()); 107 | } 108 | String[] res = builder.toString().trim().split(","); 109 | token = res[1].substring(9, res[1].length() - 1); 110 | } catch (Exception e) { 111 | log.error(e.getMessage()); 112 | } 113 | return token; 114 | } 115 | 116 | /** 117 | * http request with parameter 118 | * 119 | * @param params jsonObject 120 | * @return returns httpRequest 121 | */ 122 | private RequestSpecification httpRequest(JSONObject params) { 123 | return RestAssured 124 | .given() 125 | .with() 126 | .header("auth", authLogin()) 127 | .contentType(ContentType.JSON) 128 | .with() 129 | .body(params.toJSONString()); 130 | } 131 | 132 | /** 133 | * http request with parameter and auth token 134 | * 135 | * @param params jsonObject 136 | * @param token token 137 | * @return httpRequest 138 | */ 139 | private RequestSpecification httpRequestLogin(JSONObject params, String token) { 140 | return RestAssured 141 | .given() 142 | .with() 143 | .header("token", token) 144 | .contentType(ContentType.JSON) 145 | .with() 146 | .body(params.toJSONString()); 147 | } 148 | 149 | /** 150 | * http request without parameter 151 | * 152 | * @return returns httpRequest 153 | */ 154 | private RequestSpecification httpRequest() { 155 | return RestAssured 156 | .given() 157 | .with() 158 | .header("auth", authLogin()); 159 | } 160 | 161 | /** 162 | * http request with token 163 | * 164 | * @param token token 165 | * @return http request 166 | */ 167 | private RequestSpecification httpLogin(String token) { 168 | return RestAssured 169 | .given() 170 | .with() 171 | .header("token", token); 172 | } 173 | 174 | /** 175 | * Set base uri 176 | * 177 | * @param baseURI baseUri 178 | */ 179 | public void setBaseURI(String baseURI) { 180 | RestAssured.baseURI = baseURI; 181 | } 182 | 183 | /** 184 | * Set base path 185 | * 186 | * @param basePathTerm basepath 187 | */ 188 | public void setBasePath(String basePathTerm) { 189 | RestAssured.basePath = basePathTerm; 190 | } 191 | 192 | /** 193 | * reset base uri 194 | */ 195 | public void resetBaseURI() { 196 | RestAssured.baseURI = null; 197 | } 198 | 199 | /** 200 | * Reset base path 201 | */ 202 | public void resetBasePath() { 203 | RestAssured.basePath = null; 204 | } 205 | 206 | /** 207 | * http post 208 | * 209 | * @param params params 210 | * @param path endpoint 211 | * @return response 212 | */ 213 | protected Response httpPost(JSONObject params, String path) { 214 | return httpRequest(params).request(Method.POST, path); 215 | } 216 | 217 | /** 218 | * http post cancel 219 | * 220 | * @param params params 221 | * @param path endpoint 222 | * @param token token 223 | * @return response 224 | */ 225 | protected Response httpPostCancelPendingBets(JSONObject params, String path, String token) { 226 | return httpRequestLogin(params, token).request(Method.POST, path); 227 | } 228 | 229 | /** 230 | * http get 231 | * 232 | * @param path endpoint 233 | * @return response 234 | */ 235 | protected Response httpGet(String path) { 236 | return httpRequest().request(Method.GET, path); 237 | } 238 | 239 | /** 240 | * http get pending 241 | * 242 | * @param path endpoint 243 | * @param token token 244 | * @return response 245 | */ 246 | protected Response httpGetMicroServices(String path, String token) { 247 | return httpLogin(token).request(Method.GET, path); 248 | } 249 | 250 | /** 251 | * http delete 252 | * 253 | * @param params params 254 | * @param path endpoint 255 | * @return response 256 | */ 257 | protected Response httpDelete(JSONObject params, String path) { 258 | return httpRequest(params).request(Method.DELETE, path); 259 | } 260 | 261 | /** 262 | * http put 263 | * 264 | * @param params params 265 | * @param path endpoint 266 | * @return response 267 | */ 268 | protected Response httpPut(JSONObject params, String path) { 269 | return httpRequest(params).request(Method.PUT, path); 270 | } 271 | 272 | /** 273 | * Get Status code 274 | * 275 | * @param response response 276 | * @return status code 277 | */ 278 | protected int getStatusCode(Response response) { 279 | return response.getStatusCode(); 280 | } 281 | 282 | /** 283 | * Get headers 284 | * 285 | * @param response response 286 | * @param header header 287 | * @return header value 288 | */ 289 | private String getHeaders(Response response, String header) { 290 | return response.getHeaders().getValue(header); 291 | } 292 | 293 | /** 294 | * Response Body 295 | * 296 | * @param response response 297 | * @return responseBody 298 | */ 299 | private ResponseBody responseBody(Response response) { 300 | return response.getBody(); 301 | } 302 | 303 | /** 304 | * Json schema validation 305 | * 306 | * @param response response 307 | * @return responseBody 308 | */ 309 | private ValidatableResponse schemaValidation(Response response, String path) { 310 | return response.then().body(JsonSchemaValidator.matchesJsonSchema(path)); 311 | } 312 | 313 | /** 314 | * Get Body 315 | * 316 | * @param response response 317 | * @return preety Print 318 | */ 319 | protected String getBody(Response response) { 320 | return responseBody(response).prettyPrint(); 321 | } 322 | 323 | /** 324 | * JsonPath evaluator 325 | * 326 | * @param response response 327 | * @return jsonPath 328 | */ 329 | protected T jsonPathEvaluator(Response response, String exp) { 330 | return response.jsonPath().get(exp); 331 | } 332 | 333 | 334 | protected String sysDateFormat() { 335 | try { 336 | DateFormat date = new SimpleDateFormat("dd-MM-yyyy"); 337 | Date date1 = new Date(); 338 | String abc1 = date.format(date1); 339 | return abc1; 340 | } catch (Exception e) { 341 | log.error(e.getMessage()); 342 | } 343 | return null; 344 | } 345 | 346 | @Step("{0}") 347 | protected void log(String message) { 348 | log.info(message); 349 | } 350 | 351 | } 352 | -------------------------------------------------------------------------------- /src/main/java/com/core/ADB.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.core; 25 | 26 | import lombok.extern.slf4j.Slf4j; 27 | 28 | import java.io.File; 29 | import java.util.ArrayList; 30 | import java.util.Arrays; 31 | import java.util.List; 32 | import java.util.concurrent.TimeUnit; 33 | 34 | /** 35 | * @author Dipjyoti Metia 36 | */ 37 | @Slf4j 38 | public class ADB { 39 | 40 | private final String SDK_PATH = System.getenv("ANDROID_HOME"); 41 | private final String ADB_PATH = SDK_PATH + "platform-tools" + File.separator + "adb"; 42 | private final String EMULATOR_PATH = SDK_PATH + File.separator + "emulator"; 43 | 44 | private final String ID; 45 | 46 | public ADB(String deviceID) { 47 | ID = deviceID; 48 | } 49 | 50 | /** 51 | * launch android emulator 52 | * 53 | * @param avdName emulator name 54 | */ 55 | public void launchEmulator(String avdName) { 56 | log.info("Starting emulator for" + avdName + "...."); 57 | String[] aCommand = new String[]{EMULATOR_PATH, "-avd", avdName}; 58 | try { 59 | Process process = new ProcessBuilder(aCommand).start(); 60 | process.waitFor(180, TimeUnit.SECONDS); 61 | log.info("Emulator launched successfully"); 62 | } catch (Exception e) { 63 | log.error(e.getMessage()); 64 | } 65 | } 66 | 67 | /** 68 | * Close android emulator 69 | */ 70 | public void closeEmulator() { 71 | log.info("Closing emulator"); 72 | String[] aCommand = new String[]{EMULATOR_PATH, "emu", "kill"}; 73 | try { 74 | Process process = new ProcessBuilder(aCommand).start(); 75 | process.waitFor(1, TimeUnit.SECONDS); 76 | log.info("Emulator closed successfully"); 77 | } catch (Exception e) { 78 | log.error(e.getMessage()); 79 | } 80 | } 81 | 82 | /** 83 | * avd commands 84 | * 85 | * @param command command 86 | * @return output 87 | */ 88 | public String command(String command) throws Exception { 89 | log.debug("Formatting ADB Command: " + command); 90 | if (command.startsWith("adb")) 91 | command = command.replace("adb ", ServerManager.getAndroidHome() + "/platform-tools/adb "); 92 | else throw new Exception("This method is designed to run ADB commands only!"); 93 | log.debug("Formatted ADB Command: " + command); 94 | String output = ServerManager.runCommand(command); 95 | log.debug("Output of the ADB Command: " + output); 96 | if (output == null) return ""; 97 | else return output.trim(); 98 | } 99 | 100 | public void killServer() throws Exception { 101 | command("adb kill-server"); 102 | } 103 | 104 | public void startServer() throws Exception { 105 | command("adb start-server"); 106 | } 107 | 108 | /** 109 | * Get list of connected device 110 | * 111 | * @return devices 112 | */ 113 | public List getConnectedDevices() throws Exception { 114 | List devices = new ArrayList<>(); 115 | String output = command("adb devices"); 116 | for (String line : output.split("\n")) { 117 | line = line.trim(); 118 | if (line.endsWith("device")) devices.add(line.replace("device", "").trim()); 119 | } 120 | return devices; 121 | } 122 | 123 | /** 124 | * Get Foreground activity 125 | * 126 | * @return activity 127 | */ 128 | public String getForegroundActivity() throws Exception { 129 | return command("adb -s " + ID + " shell dumpsys window windows | grep mCurrentFocus"); 130 | } 131 | 132 | /** 133 | * Get android version 134 | * 135 | * @return version 136 | */ 137 | public String getAndroidVersionAsString() throws Exception { 138 | String output = command("adb -s " + ID + " shell getprop ro.build.version.release"); 139 | if (output.length() == 3) output += ".0"; 140 | return output; 141 | } 142 | 143 | public int getAndroidVersion() throws Exception { 144 | return Integer.parseInt(getAndroidVersionAsString().replaceAll("\\.", "")); 145 | } 146 | 147 | /** 148 | * Get installed package 149 | * 150 | * @return packages 151 | */ 152 | public List getInstalledPackages() throws Exception { 153 | List packages = new ArrayList<>(); 154 | String[] output = command("adb -s " + ID + " shell pm list packages").split("\n"); 155 | for (String packageID : output) packages.add(packageID.replace("package:", "").trim()); 156 | return packages; 157 | } 158 | 159 | /** 160 | * Open apps activity 161 | * 162 | * @param packageID package 163 | * @param activityID activity 164 | */ 165 | public void openAppsActivity(String packageID, String activityID) throws Exception { 166 | command("adb -s " + ID + " shell am start -c api.android.intent.category.LAUNCHER -a api.android.intent.action.MAIN -n " + packageID + "/" + activityID); 167 | log.info("Open apps activity"); 168 | } 169 | 170 | /** 171 | * Clear apps data 172 | * 173 | * @param packageID package 174 | */ 175 | public void clearAppsData(String packageID) throws Exception { 176 | command("adb -s " + ID + " shell pm clear " + packageID); 177 | log.info("Clear apps data " + packageID); 178 | } 179 | 180 | /** 181 | * Force stop app 182 | * 183 | * @param packageID package 184 | */ 185 | public void forceStopApp(String packageID) throws Exception { 186 | command("adb -s " + ID + " shell am force-stop " + packageID); 187 | log.info("Force stop app " + packageID); 188 | } 189 | 190 | /** 191 | * It let you change the level from 0 to 100 192 | * 193 | * @param level batteryLevel 194 | */ 195 | public void setBatteryLevel(BatteryLevelEnum level) throws Exception { 196 | String status = ""; 197 | 198 | switch (level) { 199 | case Unknown: 200 | status = "1"; 201 | break; 202 | case Charging: 203 | status = "2"; 204 | break; 205 | case Discharging: 206 | status = "3"; 207 | break; 208 | case NotCharging: 209 | status = "4"; 210 | break; 211 | case Full: 212 | status = "5"; 213 | break; 214 | default: 215 | break; 216 | } 217 | command("adb -s" + ID + "dumpsys battery set level" + status); 218 | log.info("Battery status is changed to " + status); 219 | } 220 | 221 | /** 222 | * It let you change the level to Unknown, Charging, Discharging, Not Charging or Full. 223 | * 224 | * @param status batteryStatus 225 | */ 226 | public void setBatteryStatus(String status) throws Exception { 227 | command("adb -s" + ID + "dumpsys battery set status" + status); 228 | log.info("Set battery status " + status); 229 | } 230 | 231 | /** 232 | * It let you reset the battery change made through adb. 233 | */ 234 | public void setBatteryReset() throws Exception { 235 | command("adb -s" + ID + "dumpsys battery reset"); 236 | log.info("Set battery reset"); 237 | } 238 | 239 | /** 240 | * It let you change the status of USB connection. It can be ON or OFF 241 | * 242 | * @param val switch 243 | */ 244 | public void setBatteryUSB(SwitchEnum val) throws Exception { 245 | String status = ""; 246 | 247 | switch (val) { 248 | case ON: 249 | status = "1"; 250 | break; 251 | case OFF: 252 | status = "0"; 253 | break; 254 | default: 255 | break; 256 | } 257 | command("adb -s" + ID + "dumpsys battery set usb" + status); 258 | log.info("Device USB state is " + status); 259 | } 260 | 261 | /** 262 | * Install app 263 | * 264 | * @param apkPath apk path 265 | */ 266 | public void installApp(String apkPath) throws Exception { 267 | command("adb -s " + ID + " install " + apkPath); 268 | log.info("App installed from path " + apkPath); 269 | } 270 | 271 | /** 272 | * Uninstall app 273 | * 274 | * @param packageID packageID 275 | */ 276 | public void uninstallApp(String packageID) throws Exception { 277 | command("adb -s " + ID + " uninstall " + packageID); 278 | log.info("App uninstalled " + packageID); 279 | } 280 | 281 | /** 282 | * Clear log buffer 283 | */ 284 | public void clearLogBuffer() throws Exception { 285 | command("adb -s " + ID + " shell -c"); 286 | log.info("App log buffer cleared"); 287 | } 288 | 289 | /** 290 | * Push file 291 | * 292 | * @param source source 293 | * @param target target 294 | */ 295 | public void pushFile(String source, String target) throws Exception { 296 | command("adb -s " + ID + " push " + source + " " + target); 297 | } 298 | 299 | /** 300 | * Pull file 301 | * 302 | * @param source source 303 | * @param target target 304 | */ 305 | public void pullFile(String source, String target) throws Exception { 306 | command("adb -s " + ID + " pull " + source + " " + target); 307 | } 308 | 309 | /** 310 | * Delete file 311 | * 312 | * @param target target 313 | */ 314 | public void deleteFile(String target) throws Exception { 315 | command("adb -s " + ID + " shell rm " + target); 316 | } 317 | 318 | /** 319 | * Move file 320 | * 321 | * @param source source 322 | * @param target target 323 | */ 324 | public void moveFile(String source, String target) throws Exception { 325 | command("adb -s " + ID + " shell mv " + source + " " + target); 326 | } 327 | 328 | /** 329 | * Take screenshot 330 | * 331 | * @param target target 332 | */ 333 | public void takeScreenshot(String target) throws Exception { 334 | command("adb -s " + ID + " shell screenshot " + target); 335 | log.info("Take screenshot " + target); 336 | } 337 | 338 | /** 339 | * Reboot Device 340 | */ 341 | public void rebootDevice() throws Exception { 342 | command("adb -s " + ID + " reboot"); 343 | } 344 | 345 | /** 346 | * Get Device model 347 | * 348 | * @return device details 349 | */ 350 | public String getDeviceModel() throws Exception { 351 | log.info("Get device model"); 352 | return command("adb -s " + ID + " shell getprop ro.product.model"); 353 | } 354 | 355 | /** 356 | * Get Device serial number 357 | * 358 | * @return serial number 359 | */ 360 | public String getDeviceSerialNumber() throws Exception { 361 | log.info("Get device serial number"); 362 | return command("adb -s " + ID + " shell getprop ro.serialno"); 363 | } 364 | 365 | /** 366 | * Get Device carrier 367 | * 368 | * @return carrier 369 | */ 370 | public String getDeviceCarrier() throws Exception { 371 | log.info("Get device carrier"); 372 | return command("adb -s " + ID + " shell getprop gsm.operator.alpha"); 373 | } 374 | 375 | /** 376 | * Get log process 377 | * 378 | * @return process 379 | */ 380 | public List getLogcatProcesses() throws Exception { 381 | String[] output = command("adb -s " + ID + " shell top -n 1 | grep -i 'logcat'").split("\n"); 382 | List processes = new ArrayList<>(); 383 | for (String line : output) { 384 | processes.add(line.split(" ")[0]); 385 | processes.removeAll(Arrays.asList("", null)); 386 | } 387 | return processes; 388 | } 389 | 390 | public enum BatteryLevelEnum { 391 | Unknown, Charging, Discharging, NotCharging, 392 | Full 393 | } 394 | 395 | public enum SwitchEnum { 396 | ON, OFF 397 | } 398 | } 399 | -------------------------------------------------------------------------------- /src/main/java/com/core/AppiumController.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.core; 25 | 26 | import com.config.AppConfig; 27 | import com.constants.Arg; 28 | import com.typesafe.config.ConfigFactory; 29 | import io.appium.java_client.AppiumDriver; 30 | import io.appium.java_client.android.AndroidDriver; 31 | import io.appium.java_client.android.options.UiAutomator2Options; 32 | import io.appium.java_client.ios.IOSDriver; 33 | import io.appium.java_client.ios.options.XCUITestOptions; 34 | import io.appium.java_client.remote.MobileBrowserType; 35 | import io.appium.java_client.service.local.AppiumServiceBuilder; 36 | import lombok.extern.slf4j.Slf4j; 37 | import net.lightbody.bmp.BrowserMobProxy; 38 | import net.lightbody.bmp.BrowserMobProxyServer; 39 | import net.lightbody.bmp.client.ClientUtil; 40 | import net.lightbody.bmp.proxy.CaptureType; 41 | import org.apache.commons.exec.OS; 42 | import org.openqa.selenium.Proxy; 43 | import org.openqa.selenium.remote.CapabilityType; 44 | import org.openqa.selenium.remote.DesiredCapabilities; 45 | import org.openqa.selenium.remote.RemoteWebDriver; 46 | import org.openqa.selenium.remote.service.DriverService; 47 | import org.testng.annotations.AfterTest; 48 | import org.testng.annotations.BeforeClass; 49 | import org.testng.annotations.Parameters; 50 | 51 | import java.io.File; 52 | import java.io.IOException; 53 | import java.net.URL; 54 | import java.text.DateFormat; 55 | import java.text.SimpleDateFormat; 56 | import java.util.Date; 57 | 58 | @Slf4j 59 | public class AppiumController implements Access { 60 | 61 | protected static final AppConfig appConfig = new AppConfig(ConfigFactory.load()); 62 | private static final String nodeJS = System.getenv("NODE_HOME") + "/node.exe"; 63 | private static final String appiumJS = System.getenv("APPIUM_HOME") + "/main.js"; 64 | private static RemoteWebDriver _driver; 65 | private static BrowserMobProxy server; 66 | private static DriverService service; 67 | private final String appiumPort = "4723"; 68 | private final String username = System.getenv("BROWSERSTACK_USERNAME"); 69 | private final String accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY"); 70 | private final String apk_url = System.getenv("APK_URL"); 71 | private final String ipa_url = System.getenv("IPA_URL"); 72 | private final String serverIp = "127.0.0.1"; //Local 73 | private final String cloudURL = "https://" + username + ":" + accessKey + "@hub.browserstack.com/wd/hub"; 74 | DesiredCapabilities _caps = new DesiredCapabilities(); 75 | private String testName = null; 76 | 77 | @Parameters({"device", "apps"}) 78 | @BeforeClass 79 | public void setup(String device, String apk) throws Exception { 80 | testName = this.getClass().getName().substring(24); 81 | initDriver(device, apk); 82 | } 83 | 84 | public AppiumDriver getDriver() { 85 | return (AppiumDriver) _driver; 86 | } 87 | 88 | /** 89 | * Initialize Driver 90 | * 91 | * @param device device 92 | * @param apps apk 93 | * @throws Exception exception 94 | */ 95 | private void initDriver(String device, String apps) throws Exception { 96 | try { 97 | File appDir = new File("input/app"); 98 | File app = new File(appDir, "***.apk"); 99 | String serverUrl = "http://" + serverIp + ":" + appiumPort + "/wd/hub"; 100 | switch (device) { 101 | case "NEXUS" -> { 102 | log.info("Selected device is NEXUS"); 103 | if (apps.equals("Y")) { 104 | _caps.setCapability(UiAutomator2Options.APP_OPTION, app); 105 | } 106 | _caps.setCapability(UiAutomator2Options.UDID_OPTION, NEXUS); 107 | _caps.setCapability(UiAutomator2Options.DEVICE_NAME_OPTION, "NEXUS"); 108 | _androidCapabilities(_caps); 109 | _createService().start(); 110 | log.info("Argument to driver object : " + serverUrl); 111 | _driver = new AndroidDriver(new URL(serverUrl), _caps); 112 | } 113 | case "PIXEL" -> { 114 | log.info("Selected device is PIXEL"); 115 | if (apps.equals("Y")) { 116 | _caps.setCapability(UiAutomator2Options.APP_OPTION, app); 117 | } 118 | _caps.setCapability(UiAutomator2Options.UDID_OPTION, PIXEL); 119 | _caps.setCapability(UiAutomator2Options.DEVICE_NAME_OPTION, "PIXEL"); 120 | _androidCapabilities(_caps); 121 | _createService().start(); 122 | log.info("Argument to driver object : " + serverUrl); 123 | _driver = new AndroidDriver(new URL(serverUrl), _caps); 124 | } 125 | case "samsung" -> { 126 | log.info("Selected device is SAMSUNG"); 127 | if (apps.equals("Y")) { 128 | _caps.setCapability(UiAutomator2Options.APP_OPTION, app); 129 | } 130 | _browserstackCapabilities(_caps, "samsung"); 131 | _androidCapabilities(_caps); 132 | log.info("Argument to driver object : " + cloudURL); 133 | _driver = new RemoteWebDriver(new URL(cloudURL), _caps); 134 | } 135 | case "iPhone14" -> { 136 | log.info("Selected device is IPHONE"); 137 | if (apps.equals("Y")) { 138 | _caps.setCapability(XCUITestOptions.APP_OPTION, app); 139 | } 140 | _browserstackCapabilities(_caps, "iPhone14"); 141 | _iosCapabilities(_caps); 142 | log.info("Argument to driver object : " + cloudURL); 143 | _driver = new RemoteWebDriver(new URL(cloudURL), _caps); 144 | } 145 | case "IPHONE" -> { 146 | log.info("Selected device is IPHONE"); 147 | if (apps.equals("Y")) { 148 | _caps.setCapability(XCUITestOptions.APP_OPTION, app); 149 | } 150 | _caps.setCapability(XCUITestOptions.UDID_OPTION, "iphone"); 151 | _caps.setCapability(XCUITestOptions.DEVICE_NAME_OPTION, "iphone"); 152 | _iosCapabilities(_caps); 153 | _createService().start(); 154 | log.info("Argument to driver object : " + serverUrl); 155 | _driver = new IOSDriver(new URL(serverUrl), _caps); 156 | } 157 | case "WEB" -> { 158 | log.info("Selected device is WEB"); 159 | _caps.setCapability(UiAutomator2Options.UDID_OPTION, NEXUS); 160 | _caps.setCapability(UiAutomator2Options.DEVICE_NAME_OPTION, "NEXUS"); 161 | _createService().start(); 162 | _browserCapabilities(_caps, "chrome"); 163 | log.info("Argument to driver object : " + serverUrl); 164 | _driver = new AndroidDriver(new URL(serverUrl), _caps); 165 | } 166 | } 167 | } catch (Error | 168 | Exception ex) { 169 | log.error("Appium driver could not be initialised for device", ex); 170 | throw new Exception("Appium driver could not be initialised for device: " + ex); 171 | } 172 | log.info("Driver initialized"); 173 | } 174 | 175 | /** 176 | * BrowserStack capabilities 177 | * 178 | * @param _caps capabilities 179 | */ 180 | private void _browserstackCapabilities(DesiredCapabilities _caps, String device) { 181 | switch (device) { 182 | case "samsung" -> { 183 | _caps.setCapability("platformName", "android"); 184 | _caps.setCapability("os_version", "13.0"); 185 | _caps.setCapability("device", "Samsung Galaxy S23"); 186 | _caps.setCapability("app", apk_url); 187 | } 188 | case "pixel" -> { 189 | _caps.setCapability("os_version", "9.0"); 190 | _caps.setCapability("device", "Google Pixel 3"); 191 | _caps.setCapability("app", apk_url); 192 | } 193 | case "iPhone14" -> { 194 | _caps.setCapability("platformName", "ios"); 195 | _caps.setCapability("os_version", "16"); 196 | _caps.setCapability("device", "iPhone 14"); 197 | _caps.setCapability("app", ipa_url); 198 | } 199 | default -> System.out.println("No device found"); 200 | } 201 | _caps.setCapability("browserstack.appium_version", appConfig.getAppiumVersion()); 202 | _caps.setCapability("project", appConfig.getApplicationName()); 203 | _caps.setCapability("build", testName + sysTime()); 204 | _caps.setCapability("name", testName); 205 | _caps.setCapability("isRealMobile", true); 206 | } 207 | 208 | /** 209 | * Android capabilities 210 | * 211 | * @param _caps capabilities 212 | */ 213 | private void _androidCapabilities(DesiredCapabilities _caps) { 214 | _caps.setCapability("platformName", "android"); 215 | _caps.setCapability("platformVersion", "13.0"); 216 | _caps.setCapability(UiAutomator2Options.NO_RESET_OPTION, true); 217 | _caps.setCapability(UiAutomator2Options.FULL_RESET_OPTION, false); 218 | _caps.setCapability(UiAutomator2Options.AUTO_WEB_VIEW_OPTION, false); 219 | _caps.setCapability(UiAutomator2Options.AUTO_GRANT_PERMISSIONS_OPTION, true); 220 | _caps.setCapability(UiAutomator2Options.ANDROID_INSTALL_TIMEOUT_OPTION, 60); 221 | _caps.setCapability(UiAutomator2Options.APP_PACKAGE_OPTION, "com.swaglabsmobileapp"); 222 | // _caps.setCapability(UiAutomator2Options.APP_ACTIVITY_OPTION, "com.swaglabsmobileapp.MainActivity"); 223 | } 224 | 225 | /** 226 | * IOS capabilities 227 | * 228 | * @param _caps capabilities 229 | */ 230 | private void _iosCapabilities(DesiredCapabilities _caps) { 231 | _caps.setCapability("platformName", "ios"); 232 | _caps.setCapability("platformVersion", "16"); 233 | _caps.setCapability(XCUITestOptions.FULL_RESET_OPTION, false); 234 | _caps.setCapability(XCUITestOptions.NO_RESET_OPTION, true); 235 | // _caps.setCapability(XCUITestOptions.XCODE_ORG_ID_OPTION, ""); 236 | // _caps.setCapability(XCUITestOptions.XCODE_SIGNING_ID_OPTION, ""); 237 | // _caps.setCapability(XCUITestOptions.UPDATED_WDA_BUNDLE_ID_OPTION, ""); 238 | _caps.setCapability(XCUITestOptions.AUTO_DISMISS_ALERTS_OPTION, true); 239 | _caps.setCapability(XCUITestOptions.BUNDLE_ID_OPTION, "com.saucelabs.SwagLabsMobileApp"); 240 | _caps.setCapability(XCUITestOptions.AUTOMATION_NAME_OPTION, "com.saucelabs.SwagLabsMobileApp"); 241 | } 242 | 243 | /** 244 | * Add Browser capability 245 | * 246 | * @param _caps capabilities 247 | * @param browser browser 248 | */ 249 | private void _browserCapabilities(DesiredCapabilities _caps, String browser) { 250 | if (browser.contains("chrome")) { 251 | _caps.setCapability(UiAutomator2Options.BROWSER_NAME_OPTION, MobileBrowserType.CHROME); 252 | _caps.setCapability(UiAutomator2Options.PLATFORM_VERSION_OPTION, "126"); 253 | } else { 254 | _caps.setCapability(XCUITestOptions.BROWSER_NAME_OPTION, MobileBrowserType.SAFARI); 255 | _caps.setCapability(XCUITestOptions.PLATFORM_VERSION_OPTION, "8.1"); 256 | } 257 | } 258 | 259 | /** 260 | * Add Performance capability 261 | * 262 | * @param _caps capability 263 | */ 264 | private void _performanceCapability(DesiredCapabilities _caps) { 265 | server = new BrowserMobProxyServer(); 266 | server.setTrustAllServers(true); 267 | server.start(PROXY_Port); 268 | server.enableHarCaptureTypes(CaptureType.REQUEST_CONTENT, CaptureType.RESPONSE_CONTENT); 269 | server.enableHarCaptureTypes(CaptureType.REQUEST_HEADERS, CaptureType.RESPONSE_HEADERS); 270 | Proxy proxy = ClientUtil.createSeleniumProxy(server); 271 | _caps.setCapability(CapabilityType.PROXY, proxy); 272 | server.newHar("appiumPerf.har"); 273 | } 274 | 275 | /** 276 | * Create appium driver service 277 | * 278 | * @return service 279 | */ 280 | private DriverService _createService() { 281 | service = new AppiumServiceBuilder() 282 | .usingDriverExecutable(new File(nodeJS)) 283 | .withAppiumJS(new File(appiumJS)) 284 | .withIPAddress(serverIp) 285 | .usingPort(APPIUM_Port) 286 | .withArgument(Arg.TIMEOUT, "120") 287 | .withArgument(Arg.LOG_LEVEL, "warn") 288 | .build(); 289 | return service; 290 | } 291 | 292 | /** 293 | * Stop appium server 294 | */ 295 | public void _stopAppiumServer() { 296 | if (OS.isFamilyWindows()) { 297 | Runtime runtime = Runtime.getRuntime(); 298 | try { 299 | runtime.exec("taskkill /F /IM node.exe"); 300 | runtime.exec("taskkill /F /IM cmd.exe"); 301 | } catch (IOException e) { 302 | e.printStackTrace(); 303 | } 304 | } 305 | } 306 | 307 | /** 308 | * @return sysDateTime 309 | */ 310 | private String sysTime() { 311 | try { 312 | DateFormat dates = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); 313 | Date date = new Date(); 314 | return dates.format(date); 315 | } catch (Exception e) { 316 | log.error(e.getMessage()); 317 | } 318 | return null; 319 | } 320 | 321 | @AfterTest 322 | public void tearDown() { 323 | try { 324 | // Har har = server.getHar(); 325 | // FileOutputStream fos = new FileOutputStream("C:\\temp\\perf.har"); 326 | // har.writeTo(fos); 327 | // server.stop(); 328 | _driver.quit(); 329 | _createService().stop(); 330 | _stopAppiumServer(); 331 | } catch (Exception e) { 332 | log.info("Performance test not included"); 333 | } 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /src/main/java/com/core/UserActions.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Dipjyoti Metia 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | package com.core; 26 | 27 | import com.csvreader.CsvReader; 28 | import com.csvreader.CsvWriter; 29 | import com.github.javafaker.Faker; 30 | import com.google.common.collect.ImmutableMap; 31 | import com.google.common.collect.Ordering; 32 | import io.appium.java_client.AppiumDriver; 33 | import io.appium.java_client.AppiumFluentWait; 34 | import io.appium.java_client.android.*; 35 | import io.appium.java_client.android.connection.ConnectionState; 36 | import io.appium.java_client.android.nativekey.AndroidKey; 37 | import io.appium.java_client.android.nativekey.KeyEvent; 38 | import io.appium.java_client.ios.IOSDriver; 39 | import io.qameta.allure.Step; 40 | import lombok.extern.slf4j.Slf4j; 41 | import org.apache.commons.io.FileUtils; 42 | import org.apache.commons.lang3.RandomStringUtils; 43 | import org.apache.poi.util.Units; 44 | import org.apache.poi.xwpf.usermodel.XWPFDocument; 45 | import org.apache.poi.xwpf.usermodel.XWPFParagraph; 46 | import org.apache.poi.xwpf.usermodel.XWPFRun; 47 | import org.json.JSONArray; 48 | import org.json.simple.JSONObject; 49 | import org.json.simple.parser.JSONParser; 50 | import org.openqa.selenium.*; 51 | import org.openqa.selenium.support.ui.ExpectedConditions; 52 | import org.openqa.selenium.support.ui.Wait; 53 | import org.openqa.selenium.support.ui.WebDriverWait; 54 | import org.testng.Assert; 55 | 56 | import java.io.*; 57 | import java.lang.reflect.Field; 58 | import java.sql.Connection; 59 | import java.sql.ResultSet; 60 | import java.sql.SQLException; 61 | import java.sql.Statement; 62 | import java.text.DateFormat; 63 | import java.text.SimpleDateFormat; 64 | import java.time.Duration; 65 | import java.util.NoSuchElementException; 66 | import java.util.*; 67 | import java.util.function.Function; 68 | 69 | /** 70 | * @author Dipjyoti Metia 71 | */ 72 | @Slf4j 73 | public class UserActions extends DriverManager { 74 | private static final Faker faker = new Faker(); 75 | private static String datetimeabc = null; 76 | private static int counter = 0; 77 | private final Map dicttoread = new HashMap<>(); 78 | 79 | /** 80 | * Capture screenshot 81 | * 82 | * @param name Screen name 83 | */ 84 | protected void captureScreen(String name) throws Exception { 85 | File file = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE.FILE); 86 | String dest = System.getProperty("user.dir") + "\\Screenshots\\" + name + ".png"; 87 | FileUtils.copyFile(file, new File(dest)); 88 | } 89 | 90 | /** 91 | * Capture screens in log 92 | * 93 | * @param screenShotName screenshotName 94 | * @return destinationPath 95 | * @throws IOException exception 96 | */ 97 | private String capture(String screenShotName) throws IOException { 98 | TakesScreenshot ts = driver; 99 | File source = ts.getScreenshotAs(OutputType.FILE); 100 | String dest = System.getProperty("user.dir") + "\\Reports\\Screens\\" + screenShotName + ".png"; 101 | File destination = new File(dest); 102 | FileUtils.copyFile(source, destination); 103 | return dest; 104 | } 105 | 106 | /** 107 | * Fluent wait 108 | * 109 | * @param element element 110 | * @param timeout timeoutInMilli 111 | */ 112 | private void fluentWait(WebElement element, int timeout) { 113 | Wait wait = new AppiumFluentWait<>(driver) 114 | .withTimeout(Duration.ofSeconds(timeout)) 115 | .pollingEvery(Duration.ofMillis(5)) 116 | .ignoring(NoSuchElementException.class); 117 | wait.until(ExpectedConditions.elementToBeClickable(element)); 118 | } 119 | 120 | /** 121 | * Get Mobile ElementBY 122 | * 123 | * @param byElement byElement 124 | * @param mobileBy mobileBy 125 | * @return by 126 | */ 127 | private By getWebElementBy(String byElement, MobileBy mobileBy) { 128 | By by = null; 129 | switch (mobileBy) { 130 | case ID -> by = (By) driver.findElement(By.id(byElement)); 131 | case XPATH -> by = (By) driver.findElement(By.xpath(byElement)); 132 | case NAME -> by = (By) driver.findElement(By.name(byElement)); 133 | case CLASS -> by = (By) driver.findElement(By.className(byElement)); 134 | default -> log.info("no default element selected"); 135 | } 136 | return by; 137 | } 138 | 139 | /** 140 | * Get mobile element 141 | * 142 | * @param mobileElement mobileElement 143 | * @param mobileBy typeOf element 144 | * @return element 145 | * @throws Exception exception 146 | */ 147 | private WebElement getWebElement(String mobileElement, MobileBy mobileBy) throws Exception { 148 | WebElement element = null; 149 | switch (mobileBy) { 150 | case XPATH -> element = driver.findElement(By.xpath(mobileElement)); 151 | case ID -> element = driver.findElement(By.id(mobileElement)); 152 | case NAME -> element = driver.findElement(By.name(mobileElement)); 153 | case CLASS -> element = driver.findElement(By.className(mobileElement)); 154 | default -> log.info("Element type not found"); 155 | } 156 | if (element == null) { 157 | log.error("Mobile element not found"); 158 | } 159 | return element; 160 | } 161 | 162 | /** 163 | * Click on element 164 | * 165 | * @param element element 166 | */ 167 | protected void click(WebElement element) { 168 | try { 169 | fluentWait(element, 10); 170 | element.click(); 171 | log.info("Clicked on element: " + element); 172 | } catch (ElementNotInteractableException e) { 173 | log.error("Element not visible", e); 174 | } 175 | } 176 | 177 | /** 178 | * Click 179 | * 180 | * @param element mobileElement 181 | * @param elementType elementType 182 | */ 183 | public void click(String element, MobileBy elementType) throws Exception { 184 | click(getMobileElement(element, elementType)); 185 | } 186 | 187 | /** 188 | * Click on element with timeout 189 | * 190 | * @param element element 191 | * @param timeOut timeOut 192 | */ 193 | public void click(WebElement element, int timeOut) { 194 | try { 195 | fluentWait(element, timeOut); 196 | element.click(); 197 | log.info("Clicked on element: " + element); 198 | } catch (ElementNotInteractableException e) { 199 | log.error("Element not visible", e); 200 | } 201 | } 202 | 203 | /** 204 | * Enter value in text field 205 | * 206 | * @param element element 207 | * @param value value 208 | */ 209 | protected void enter(WebElement element, String value) { 210 | try { 211 | fluentWait(element, 10); 212 | element.click(); 213 | element.sendKeys(value); 214 | log.info("Entered value: " + value + "on element: " + element); 215 | } catch (ElementNotInteractableException e) { 216 | log.error("Element not visible", e); 217 | } 218 | } 219 | 220 | /** 221 | * Enter 222 | * 223 | * @param element mobileElement 224 | * @param elementType elementType 225 | * @param value value 226 | */ 227 | protected void enter(String element, MobileBy elementType, String value) throws Exception { 228 | enter(getMobileElement(element, elementType), value); 229 | } 230 | 231 | /** 232 | * Get mobile element 233 | * 234 | * @param mobileElement mobileElement 235 | * @param mobileBy typeOf element 236 | * @return element 237 | * @throws Exception exception 238 | */ 239 | private WebElement getMobileElement(String mobileElement, MobileBy mobileBy) throws Exception { 240 | WebElement element = null; 241 | switch (mobileBy) { 242 | case XPATH -> element = driver.findElement(By.xpath(mobileElement)); 243 | case ID -> element = driver.findElement(By.id(mobileElement)); 244 | case NAME -> element = driver.findElement(By.name(mobileElement)); 245 | case CLASS -> element = driver.findElement(By.className(mobileElement)); 246 | default -> log.info("Element type not found"); 247 | } 248 | if (element == null) { 249 | log.error("Mobile element not found"); 250 | } 251 | return element; 252 | } 253 | 254 | 255 | /** 256 | * Get Mobile ElementBY 257 | * 258 | * @param byElement byElement 259 | * @param mobileBy mobileBy 260 | * @return by 261 | */ 262 | private By getMobileElementBy(String byElement, MobileBy mobileBy) { 263 | By by = null; 264 | switch (mobileBy) { 265 | case ID -> by = (By) driver.findElement(By.id(byElement)); 266 | case XPATH -> by = (By) driver.findElement(By.xpath(byElement)); 267 | case NAME -> by = (By) driver.findElement(By.name(byElement)); 268 | case CLASS -> by = (By) driver.findElement(By.className(byElement)); 269 | default -> log.info("no default element selected"); 270 | } 271 | return by; 272 | } 273 | 274 | /** 275 | * Element is displaying 276 | * 277 | * @param element element 278 | * @return boolean 279 | */ 280 | public boolean isDisplayed(WebElement element) throws Exception { 281 | if (element.isDisplayed()) { 282 | log.info(element + ": element is Displayed"); 283 | return true; 284 | } else { 285 | log.error("Element is not displayed"); 286 | } 287 | return false; 288 | } 289 | 290 | /** 291 | * Element is enabled 292 | * 293 | * @param element element 294 | * @return boolean 295 | */ 296 | protected boolean isEnabled(WebElement element) { 297 | if (element.isEnabled()) { 298 | log.info(element + ": element is Enabled"); 299 | return true; 300 | } 301 | return false; 302 | } 303 | 304 | 305 | /** 306 | * Element is Selected 307 | * 308 | * @param element element 309 | * @return boolean 310 | */ 311 | protected boolean isSelected(WebElement element) { 312 | if (element.isSelected()) { 313 | log.info(element + ": element is Selected"); 314 | return true; 315 | } 316 | return false; 317 | } 318 | 319 | /** 320 | * Is Exists 321 | * 322 | * @param element mobileElement 323 | * @param elementType elementType 324 | * @return boolean 325 | */ 326 | protected boolean isExist(String element, MobileBy elementType) { 327 | boolean returnValue = false; 328 | switch (elementType) { 329 | case XPATH: 330 | if (driver.findElements(By.xpath(element)).size() != 0) { 331 | log.info(element + ": element is exists"); 332 | returnValue = true; 333 | break; 334 | } 335 | case ID: 336 | if (driver.findElements(By.id(element)).size() != 0) { 337 | log.info(element + ": element is exists"); 338 | returnValue = true; 339 | break; 340 | } 341 | case CLASS: 342 | if (driver.findElements(By.className(element)).size() != 0) { 343 | log.info(element + ": element is exists"); 344 | returnValue = true; 345 | break; 346 | } 347 | default: 348 | log.info("Element type is not available"); 349 | break; 350 | } 351 | return returnValue; 352 | } 353 | 354 | 355 | /** 356 | * is present 357 | * 358 | * @param elements elements 359 | * @return boolean 360 | */ 361 | protected boolean isPresent(List elements) { 362 | if (elements.size() != 0) { 363 | log.info(elements + ": element is Present"); 364 | return true; 365 | } 366 | return false; 367 | } 368 | 369 | /** 370 | * Get page source 371 | * 372 | * @return pageSource 373 | */ 374 | public String getPageSource() { 375 | return driver.getPageSource(); 376 | } 377 | 378 | 379 | /** 380 | * Verify text content 381 | * 382 | * @param actual actual 383 | * @param expected expected 384 | */ 385 | public void verifyTextContent(String actual, String expected) { 386 | Assert.assertEquals(actual, expected); 387 | } 388 | 389 | /** 390 | * Get Text content 391 | * 392 | * @param containText contain text 393 | * @return text 394 | */ 395 | public String getTextContent(String containText) { 396 | return driver.findElement(By.xpath("//*[contains(text(),'" + containText + "')]")).getText(); 397 | } 398 | 399 | /** 400 | * Is Text present 401 | * 402 | * @param containsText contains text 403 | * @return boolean 404 | */ 405 | public boolean isTextPresent(String containsText) throws Exception { 406 | if (driver.getPageSource().contains(containsText)) { 407 | return true; 408 | } else { 409 | log.error("Text is not present"); 410 | } 411 | return false; 412 | } 413 | 414 | /** 415 | * NetworkSPeed Android 416 | * 417 | * @param networkSpeed networkSpeed 418 | */ 419 | public void networkSpeedAndroid(String networkSpeed) { 420 | switch (networkSpeed) { 421 | case "FULL" -> ((AndroidDriver) driver).setNetworkSpeed(NetworkSpeed.FULL); 422 | case "GPRS" -> ((AndroidDriver) driver).setNetworkSpeed(NetworkSpeed.GPRS); 423 | case "HSDPA" -> ((AndroidDriver) driver).setNetworkSpeed(NetworkSpeed.HSDPA); 424 | case "LTE" -> ((AndroidDriver) driver).setNetworkSpeed(NetworkSpeed.LTE); 425 | default -> log.info("network speed not available"); 426 | } 427 | } 428 | 429 | /** 430 | * SignalStrength Android 431 | * 432 | * @param signalStrength signalStrength 433 | */ 434 | public void signalStrengthAndroid(String signalStrength) { 435 | switch (signalStrength) { 436 | case "GREAT" -> ((AndroidDriver) driver).setGsmSignalStrength(GsmSignalStrength.GREAT); 437 | case "MODERATE" -> ((AndroidDriver) driver).setGsmSignalStrength(GsmSignalStrength.MODERATE); 438 | case "NONE" -> ((AndroidDriver) driver).setGsmSignalStrength(GsmSignalStrength.NONE_OR_UNKNOWN); 439 | default -> log.info("Signal Strength not available"); 440 | } 441 | } 442 | 443 | /** 444 | * SignalStrength Android 445 | * 446 | * @param voiceState voiceState 447 | */ 448 | public void voiceStateAndroid(String voiceState) { 449 | switch (voiceState) { 450 | case "UNREGISTERED" -> ((AndroidDriver) driver).setGsmVoice(GsmVoiceState.UNREGISTERED); 451 | case "ROAMING" -> ((AndroidDriver) driver).setGsmVoice(GsmVoiceState.ROAMING); 452 | case "SEARCHING" -> ((AndroidDriver) driver).setGsmVoice(GsmVoiceState.SEARCHING); 453 | default -> log.info("Voice state not available"); 454 | } 455 | } 456 | 457 | /** 458 | * SignalStrength Android 459 | * 460 | * @param powerState powerState 461 | */ 462 | public void powerStateAndroid(String powerState) { 463 | switch (powerState) { 464 | case "ON" -> ((AndroidDriver) driver).setPowerAC(PowerACState.ON); 465 | case "OFF" -> ((AndroidDriver) driver).setPowerAC(PowerACState.OFF); 466 | default -> log.info("Voice state not available"); 467 | } 468 | } 469 | 470 | /** 471 | * Connection State 472 | * 473 | * @param connectionState connectionState 474 | * @param enabled boolean 475 | */ 476 | public void connectionStateAndroid(String connectionState, boolean enabled) { 477 | switch (connectionState) { 478 | case "AIRPLANE" -> { 479 | if (enabled) { 480 | ((AndroidDriver) driver).setConnection(new ConnectionState(ConnectionState.AIRPLANE_MODE_MASK)).isAirplaneModeEnabled(); 481 | } 482 | ((AndroidDriver) driver).setConnection(new ConnectionState(ConnectionState.AIRPLANE_MODE_MASK)); 483 | } 484 | case "DATA" -> { 485 | if (enabled) { 486 | ((AndroidDriver) driver).setConnection(new ConnectionState(ConnectionState.DATA_MASK)).isDataEnabled(); 487 | } 488 | ((AndroidDriver) driver).setConnection(new ConnectionState(ConnectionState.DATA_MASK)); 489 | } 490 | case "WIFI" -> { 491 | if (enabled) { 492 | ((AndroidDriver) driver).setConnection(new ConnectionState(ConnectionState.WIFI_MASK)).isWiFiEnabled(); 493 | } 494 | ((AndroidDriver) driver).setConnection(new ConnectionState(ConnectionState.WIFI_MASK)); 495 | } 496 | default -> log.info("Connection state not available"); 497 | } 498 | } 499 | 500 | /** 501 | * Press Back 502 | */ 503 | public void pressBackAndroid() { 504 | ((AndroidDriver) driver).pressKey(new KeyEvent(AndroidKey.BACK)); 505 | log.info("Press Back"); 506 | } 507 | 508 | /** 509 | * Shake Device 510 | */ 511 | public void shakeDeviceIos() { 512 | ((IOSDriver) driver).shake(); 513 | log.info("Shake Device"); 514 | } 515 | 516 | /** 517 | * Press Back 518 | */ 519 | public void setKeyboardCorrectionIos(boolean bool) { 520 | ((IOSDriver) driver).setKeyboardAutocorrection(bool); 521 | log.info("Shake Device"); 522 | } 523 | 524 | /** 525 | * Swipe Down 526 | */ 527 | public void swipeDown() { 528 | driver.executeScript("scroll", ImmutableMap.of("direction", "down")); 529 | log.info("Swipe Down"); 530 | } 531 | 532 | /** 533 | * Swipe Up 534 | */ 535 | public void swipeUP() { 536 | driver.executeScript("scroll", ImmutableMap.of("direction", "up")); 537 | log.info("Swipe Up"); 538 | } 539 | 540 | /** 541 | * Accept Alert 542 | */ 543 | public void acceptAlert() { 544 | driver.executeScript("acceptAlert"); 545 | log.info("Accept Alert"); 546 | } 547 | 548 | /** 549 | * Dismiss Alert 550 | */ 551 | public void dismissAlert() { 552 | driver.executeScript("dismissAlert"); 553 | log.info("Dismiss Alert"); 554 | } 555 | 556 | /** 557 | * Get text from the element 558 | * 559 | * @param element element 560 | * @return string 561 | */ 562 | protected String getText(WebElement element) { 563 | try { 564 | String value; 565 | fluentWait(element, 10); 566 | value = element.getText(); 567 | return value; 568 | } catch (ElementNotInteractableException e) { 569 | log.error("Element not visible", e); 570 | } 571 | return null; 572 | } 573 | 574 | /** 575 | * Get text from the element 576 | * 577 | * @param element element 578 | * @return string 579 | */ 580 | protected String getTextByXpath(String element) { 581 | try { 582 | String value; 583 | value = driver.findElement(By.xpath(element)).getText(); 584 | return value; 585 | } catch (ElementNotInteractableException e) { 586 | log.error("Element not visible", e); 587 | } 588 | return null; 589 | } 590 | 591 | /** 592 | * Get attribute text from the element 593 | * 594 | * @param element element 595 | * @return string 596 | */ 597 | protected String getAttribute(WebElement element) { 598 | try { 599 | String value; 600 | fluentWait(element, 10); 601 | value = element.getAttribute("text"); 602 | return value; 603 | } catch (ElementNotInteractableException e) { 604 | log.error("Element not visible", e); 605 | } 606 | return null; 607 | } 608 | 609 | /** 610 | * Scroll to specific location 611 | * 612 | * @param element element 613 | * @param value location 614 | */ 615 | public void scrollToLocation(WebElement element, int value) { 616 | try { 617 | JavascriptExecutor js = driver; 618 | HashMap scrollElement = new HashMap(); 619 | scrollElement.put("startX", 0.50); 620 | scrollElement.put("startY", 0.95); 621 | scrollElement.put("endX", 0.50); 622 | scrollElement.put("endY", 0.01); 623 | scrollElement.put("duration", 3.0); 624 | js.executeScript("swipe", scrollElement); 625 | } catch (ElementNotInteractableException e) { 626 | log.error("Element not visible", e); 627 | } 628 | } 629 | 630 | /** 631 | * Click on back button 632 | */ 633 | public void clickBackButton() { 634 | driver.navigate().back(); //Closes keyboard 635 | } 636 | 637 | /** 638 | * Threaded sleep 639 | * 640 | * @param time time 641 | */ 642 | protected void sleep(int time) { 643 | try { 644 | Thread.sleep(time); 645 | } catch (Exception e) { 646 | log.error(e.getMessage()); 647 | } 648 | } 649 | 650 | /** 651 | * Log allure 652 | * 653 | * @param message log message 654 | */ 655 | @Step("{0}") 656 | private void log(String message) { 657 | log.info(message); 658 | } 659 | 660 | 661 | /** 662 | * Get coordinate by id 663 | * 664 | * @param byId Byid 665 | * @return point 666 | */ 667 | public Point getCoordinates(String byId) { 668 | WebElement element = driver.findElement(By.id(byId)); 669 | Point location = element.getLocation(); 670 | System.out.println(location); 671 | return location; 672 | } 673 | 674 | public void outputIfMatchPassOrFail(String expectedValue, String actualValue) { 675 | String result; 676 | if (expectedValue.trim().contains(actualValue.trim())) { 677 | result = "(PASS)"; 678 | } else { 679 | result = "(FAIL)"; 680 | } 681 | log.info("Verifying Expected Value Matches Actual Value:"); 682 | log.info("\t* Expected Value: " + expectedValue); 683 | log.info("\t* Actual Value: " + actualValue); 684 | log.info("===> " + result); 685 | } 686 | 687 | /** 688 | * Check List is sorted 689 | * 690 | * @param ListToSort lists 691 | * @return boolean 692 | */ 693 | public boolean checkListIsSorted(List ListToSort) { 694 | if (ListToSort.size() > 0) { 695 | try { 696 | if (Ordering.natural().isOrdered(ListToSort)) { 697 | log.info("Check sorting ,List is sorted"); 698 | return true; 699 | } else { 700 | log.error("Check Sorting,List is not sorted"); 701 | return false; 702 | } 703 | } catch (Exception e) { 704 | log.error(e.getMessage()); 705 | } 706 | } else { 707 | log.info("There are no elements in the list"); 708 | } 709 | return false; 710 | } 711 | 712 | /** 713 | * Generate random Data 714 | * 715 | * @param randomType randomType 716 | * @return value 717 | */ 718 | protected String generateRandomData(String randomType) { 719 | String value = null; 720 | switch (randomType) { 721 | case "FirstName" -> { 722 | value = "testauto" + faker.name().firstName(); 723 | log.info("FirstName: " + value); 724 | } 725 | case "LastName" -> { 726 | value = faker.name().lastName(); 727 | log.info("LastName: " + value); 728 | } 729 | case "UserName" -> { 730 | value = RandomStringUtils.randomAlphabetic(6); 731 | log.info("Username: " + value); 732 | } 733 | case "Email" -> { 734 | value = "testauto" + faker.internet().emailAddress(); 735 | log.info("EmailAddress: " + value); 736 | } 737 | case "Mobile" -> { 738 | value = "0" + RandomStringUtils.randomNumeric(9); 739 | log.info("MobileNo: " + value); 740 | } 741 | default -> log.info("Random type not found"); 742 | } 743 | return value; 744 | } 745 | 746 | /** 747 | * Generate random string 748 | * 749 | * @param count count 750 | * @return value 751 | */ 752 | public String generateRandomString(int count) { 753 | String name = RandomStringUtils.randomAlphabetic(count); 754 | log.info(name); 755 | return name; 756 | } 757 | 758 | /** 759 | * Generate random ascii 760 | * 761 | * @param count count 762 | * @return value 763 | */ 764 | public String generateRandomAscii(int count) { 765 | String name = RandomStringUtils.randomAscii(count); 766 | log.info(name); 767 | return name; 768 | } 769 | 770 | /** 771 | * Get user Data 772 | * 773 | * @param threadID threadId 774 | * @return data 775 | */ 776 | public JSONObject getUserData(int threadID) { 777 | JSONParser parser = new JSONParser(); 778 | try { 779 | Object obj = parser.parse(new FileReader(System.getProperty("user.dir") + "/" + 780 | "credentials.json")); 781 | JSONObject jsonObject = (JSONObject) obj; 782 | JSONArray msg = (JSONArray) jsonObject.get("credentials"); 783 | JSONObject a = (JSONObject) msg.get(threadID); 784 | log.info(msg.get(threadID).toString()); 785 | return a; 786 | 787 | } catch (Exception e) { 788 | log.error(e.getMessage()); 789 | } 790 | return null; 791 | } 792 | 793 | /** 794 | * Get data from csv 795 | * 796 | * @param t_testcaseName testcaseName 797 | * @param t_fieldName filedName 798 | * @param t_instance instance 799 | * @return fieldValue 800 | */ 801 | protected String getData(String t_testcaseName, String t_fieldName, int t_instance) { 802 | try { 803 | int flag = 0; 804 | CsvReader csvreaderobj = new CsvReader("input/DataSheet.csv"); 805 | csvreaderobj.readHeaders(); 806 | while (csvreaderobj.readRecord()) { 807 | String p_testcaseName = csvreaderobj.get("TestcaseName").trim(); 808 | String p_testcaseInstance = csvreaderobj.get("TestcaseInstance").trim(); 809 | if ((t_testcaseName.equalsIgnoreCase(p_testcaseName)) && (t_instance == Integer.parseInt(p_testcaseInstance))) { 810 | for (int i = 0; i < csvreaderobj.getColumnCount() / 2 + 1; i++) { 811 | String p_field = csvreaderobj.get("Field" + i).trim(); 812 | String p_objectProperty = csvreaderobj.get("Value" + i).trim(); 813 | dicttoread.put(p_field, p_objectProperty); 814 | } 815 | flag = 0; 816 | break; 817 | } else { 818 | flag = 1; 819 | } 820 | } 821 | if (flag == 1) { 822 | log.info("Not data present for testname" + t_testcaseName); 823 | } 824 | } catch (IOException ef) { 825 | log.error(ef.getMessage()); 826 | } 827 | return (String) dicttoread.get(t_fieldName); 828 | } 829 | 830 | /** 831 | * GetData from SQlite 832 | * 833 | * @param testCaseName test name 834 | * @param filedName filed name 835 | * @return data 836 | */ 837 | protected String getData(String testCaseName, String filedName) { 838 | String filed = filedName; 839 | String testCase = testCaseName; 840 | String url = "jdbc:sqlite:input/testdata"; 841 | Connection conn = null; 842 | ResultSet rs = null; 843 | Statement stmt = null; 844 | String value = null; 845 | try { 846 | conn = java.sql.DriverManager.getConnection(url); 847 | if (conn != null) { 848 | stmt = conn.createStatement(); 849 | rs = stmt.executeQuery("SELECT " + filed + " FROM testdata WHERE TestcaseName = '" + testCase + "'"); 850 | while (rs.next()) { 851 | log.info(rs.getString(filed)); 852 | value = rs.getString(filed); 853 | } 854 | } 855 | assert conn != null; 856 | conn.close(); 857 | rs.close(); 858 | stmt.close(); 859 | } catch (SQLException e) { 860 | log.error(e.getMessage()); 861 | } 862 | return value; 863 | } 864 | 865 | /** 866 | * Write data to csv 867 | * 868 | * @param t_testcasename testcaseName 869 | * @param t_field filedName 870 | * @param t_value value 871 | * @param t_instance instance 872 | */ 873 | protected void writeData(String t_testcasename, String t_field, String t_value, int t_instance) { 874 | try { 875 | int flag = 0; 876 | int i; 877 | int P_valuenotduplicated = 0; 878 | CsvWriter csvOutput = new CsvWriter(new FileWriter("input\\Datasheet1.csv", false), ','); 879 | CsvReader csvobj = new CsvReader("input\\Datasheet.csv"); 880 | csvobj.readHeaders(); 881 | String FileContentPerRow = csvobj.getRawRecord(); 882 | csvOutput.writeRecord(FileContentPerRow.split(",")); 883 | while (csvobj.readRecord()) { 884 | FileContentPerRow = csvobj.getRawRecord(); 885 | String p_testcaseName = csvobj.get("TestcaseName").trim(); 886 | String p_testcaseInstance = csvobj.get("TestcaseInstance").trim(); 887 | if (t_testcasename.equalsIgnoreCase(p_testcaseName) && (t_instance == Integer.parseInt(p_testcaseInstance))) { 888 | for (i = 1; i < csvobj.getColumnCount() / 2 + 1; i++) { 889 | String p_filed = csvobj.get("Field" + i).trim(); 890 | if (p_filed.equalsIgnoreCase(t_field)) { 891 | String p_field1 = csvobj.get("Value" + i).trim(); 892 | dicttoread.put(t_field, t_value); 893 | log.info("value for the field: " + t_field + " is updated to: " + t_value + " Successfully"); 894 | String stp = CsvWriter.replace(FileContentPerRow, t_field + "," + p_field1, t_field + "," + t_value); 895 | log.info(stp); 896 | FileContentPerRow = stp; 897 | P_valuenotduplicated = 1; 898 | } 899 | } 900 | if (P_valuenotduplicated == 0) { 901 | String p_field1 = csvobj.get("Value" + (i - 1)).trim(); 902 | dicttoread.put(t_field, t_value); 903 | String stp1 = CsvWriter.replace(FileContentPerRow, p_field1, p_field1 + "," + t_field + "," + t_value); 904 | log.info(stp1); 905 | FileContentPerRow = stp1; 906 | log.info("New Field: " + t_field + " is added successfully with value: " + t_value); 907 | } 908 | flag = 1; 909 | } 910 | csvOutput.writeRecord(FileContentPerRow.split(",")); 911 | } 912 | csvOutput.flush(); 913 | csvOutput.close(); 914 | csvobj.close(); 915 | renameCsvFile("input\\Datasheet1.csv", "input\\Datasheet.csv"); 916 | if (flag == 0) { 917 | log.info("No data present for the testname"); 918 | } 919 | } catch (Exception e) { 920 | log.error(e.getMessage()); 921 | } 922 | } 923 | 924 | /** 925 | * WriteData to Sqlite 926 | * 927 | * @param testCaseName testName 928 | * @param fieldName fieldName 929 | * @param updatedValue updated Value 930 | */ 931 | protected void writeData(String testCaseName, String fieldName, String updatedValue) { 932 | String url = "jdbc:sqlite:input/testdata"; 933 | String selectQuery = "SELECT " + fieldName + " FROM testdata WHERE TestcaseName='" + testCaseName + "'"; 934 | String query = "UPDATE testdata SET " + fieldName + "='" + updatedValue + "' WHERE TestcaseName='" + testCaseName + "'"; 935 | Connection conn = null; 936 | ResultSet rs = null; 937 | Statement stmt = null; 938 | try { 939 | conn = java.sql.DriverManager.getConnection(url); 940 | if (conn != null) { 941 | stmt = conn.createStatement(); 942 | stmt.executeUpdate(query); 943 | rs = stmt.executeQuery(selectQuery); 944 | while (rs.next()) { 945 | System.out.println(rs.getString(fieldName)); 946 | log.info("New Field: " + fieldName + " is added successfully with value: " + rs.getString(fieldName)); 947 | } 948 | } 949 | rs.close(); 950 | stmt.close(); 951 | conn.close(); 952 | } catch (SQLException e) { 953 | log.error(e.getMessage()); 954 | } 955 | } 956 | 957 | /** 958 | * Rename csv file 959 | * 960 | * @param source sourceFile 961 | * @param dest destinationFile 962 | */ 963 | private boolean renameCsvFile(String source, String dest) { 964 | boolean b = false; 965 | try { 966 | boolean b1 = false; 967 | File file1 = new File(dest); 968 | System.gc(); 969 | file1.setWritable(true); 970 | Thread.sleep(500); 971 | if (file1.exists()) { 972 | b1 = file1.delete(); 973 | } 974 | Thread.sleep(500); 975 | log.info(String.valueOf(b1)); 976 | File file = new File(source); 977 | b = file.renameTo(new File(dest)); 978 | log.info(String.valueOf(b)); 979 | } catch (Exception e) { 980 | log.error(e.getMessage()); 981 | } 982 | return b; 983 | } 984 | 985 | /** 986 | * Capture image 987 | * 988 | * @param p_testcaseName testcaseName 989 | */ 990 | public void captureImage(String p_testcaseName) { 991 | try { 992 | counter = counter + 1; 993 | File src = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); 994 | FileUtils.copyFile(src, new File(("ScreensDoc\\" + p_testcaseName + "\\" + datetimeabc + "\\" + counter + ".png"))); 995 | } catch (Exception e) { 996 | log.error("Capture screenShot failed", e); 997 | } 998 | } 999 | 1000 | /** 1001 | * Create image doc 1002 | * 1003 | * @param p_testcaseName1 testcaseName 1004 | */ 1005 | protected void createImageDoc(String p_testcaseName1) { 1006 | try (XWPFDocument doc = new XWPFDocument()) { 1007 | XWPFParagraph p = doc.createParagraph(); 1008 | XWPFRun r = p.createRun(); 1009 | for (int i = 1; i <= counter; i++) { 1010 | String path = "ScreensDoc\\" + p_testcaseName1 + "\\" + datetimeabc + "\\" + i + ".png"; 1011 | try (FileInputStream pic = new FileInputStream(path)) { 1012 | r.addBreak(); 1013 | r.addCarriageReturn(); 1014 | r.addPicture(pic, XWPFDocument.PICTURE_TYPE_PNG, "ScreensDoc\\" + p_testcaseName1 + "\\" + 1015 | datetimeabc + "\\" + i + ".png", Units.toEMU(300), Units.toEMU(400)); 1016 | FileOutputStream out = new FileOutputStream("ScreensDoc\\" + p_testcaseName1 + "\\" + datetimeabc + "\\" + p_testcaseName1 + ".docx"); 1017 | doc.write(out); 1018 | pic.close(); 1019 | out.close(); 1020 | } catch (IOException io) { 1021 | log.error(io.getMessage()); 1022 | } 1023 | } 1024 | for (int i = 1; i <= counter; i++) { 1025 | File src1 = new File("ScreensDoc\\" + p_testcaseName1 + "\\" + datetimeabc + "\\" + i + ".png"); 1026 | deleteDir(src1); 1027 | } 1028 | counter = 0; 1029 | } catch (Exception e) { 1030 | log.error(e.getMessage()); 1031 | } 1032 | } 1033 | 1034 | /** 1035 | * Delete dir 1036 | * 1037 | * @param file fileName 1038 | */ 1039 | private void deleteDir(File file) { 1040 | File[] contents = file.listFiles(); 1041 | if (contents != null) { 1042 | for (File f : contents) { 1043 | f.delete(); 1044 | } 1045 | } 1046 | file.delete(); 1047 | } 1048 | 1049 | /** 1050 | * System date format 1051 | */ 1052 | protected void systemDateFormat() { 1053 | String abc1 = null; 1054 | try { 1055 | DateFormat date = new SimpleDateFormat("yyyy.MM.dd_hh.mm"); 1056 | Date date1 = new Date(); 1057 | abc1 = date.format(date1); 1058 | datetimeabc = "Run_" + abc1; 1059 | } catch (Exception e) { 1060 | log.error(e.getMessage()); 1061 | } 1062 | } 1063 | 1064 | /** 1065 | * SQL server windows authentication 1066 | */ 1067 | public void windowsAuthentication() { 1068 | String path = System.getProperty("java.library.path"); 1069 | path = "input/sqljdbc_auth.dll" + ";" + path; 1070 | System.setProperty("java.library.path", path); 1071 | try { 1072 | final Field sysPathFiled = ClassLoader.class.getDeclaredField("sys_paths"); 1073 | sysPathFiled.setAccessible(true); 1074 | sysPathFiled.set(null, null); 1075 | } catch (Exception e) { 1076 | log.error(e.getMessage()); 1077 | } 1078 | } 1079 | 1080 | /** 1081 | * Wait for page to get loaded 1082 | * 1083 | * @param id locatorId 1084 | */ 1085 | private void waitForPageToLoad(WebElement id) { 1086 | WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(25)); 1087 | wait.until((Function) ExpectedConditions.elementToBeClickable(id)); 1088 | } 1089 | 1090 | /** 1091 | * Wait for element to disappear 1092 | * 1093 | * @param id locatorId 1094 | */ 1095 | public void waitForElementToDisAppear(String id) { 1096 | WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(25)); 1097 | wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id(id))); 1098 | } 1099 | 1100 | /** 1101 | * Wait for element to be displayed 1102 | * 1103 | * @param arg element 1104 | */ 1105 | public void waitForElement(WebElement arg) { 1106 | waitForPageToLoad(arg); 1107 | } 1108 | 1109 | protected void catchBlock(Exception e) { 1110 | counter = 0; 1111 | log.error("Error Description", e); 1112 | Assert.fail("TestCase Failed", e); 1113 | } 1114 | 1115 | public enum MobileBy { 1116 | XPATH, ID, NAME, CLASS, ACCESS_ID 1117 | } 1118 | 1119 | } --------------------------------------------------------------------------------