diff --git a/.gitignore b/.gitignore index c899a1f..a35e545 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,12 @@ allure-results -node_modules \ No newline at end of file +node_modules +custom_wdio_E2E/ios/logs +custom_wdio_E2E/ios/local.log +custom_wdio_E2E/ios/node_modules +custom_wdio_E2E/android/logs +custom_wdio_E2E/android/local.log +custom_wdio_E2E/android/node_modules +custom_wdio_E2E/android/node_modules +custom_wdio_E2E/ios/node_modules +.gitignore +local.log diff --git a/custom_wdio_E2E/android/app/WikipediaSample.apk b/custom_wdio_E2E/android/app/WikipediaSample.apk new file mode 100644 index 0000000..03d19e6 Binary files /dev/null and b/custom_wdio_E2E/android/app/WikipediaSample.apk differ diff --git a/custom_wdio_E2E/android/configs/wdio.base.conf.js b/custom_wdio_E2E/android/configs/wdio.base.conf.js new file mode 100644 index 0000000..911bac0 --- /dev/null +++ b/custom_wdio_E2E/android/configs/wdio.base.conf.js @@ -0,0 +1,19 @@ +const path = require('path'); + +exports.config = { + user: process.env.BROWSERSTACK_USERNAME || 'YOUR_USERNAME', + key: process.env.BROWSERSTACK_ACCESS_KEY || 'YOUR_ACCESS_KEY', + + specs: [ + '../features/**/*.feature' // Adjust this to the correct feature file location + ], + + logLevel: 'info', + framework: 'cucumber', + + cucumberOpts: { + require: [path.resolve(__dirname, '../step-definitions/**/*.js')], + timeout: 60000 + } + }; + \ No newline at end of file diff --git a/custom_wdio_E2E/android/configs/wdio.browserstack.local.conf.js b/custom_wdio_E2E/android/configs/wdio.browserstack.local.conf.js new file mode 100644 index 0000000..8d9ec78 --- /dev/null +++ b/custom_wdio_E2E/android/configs/wdio.browserstack.local.conf.js @@ -0,0 +1,52 @@ +const baseConfig = require('./wdio.base.conf'); + +exports.config = { + ...baseConfig.config, + services: [ + ['browserstack', + { + browserstackLocal: true, + buildIdentifier: '#${BUILD_NUMBER}', + opts: { + forcelocal: true, + localIdentifier: 'webdriverio-browserstack-repo4' + } + } + ], + ], + + + commonCapabilities: { + platformName: 'android', + 'appium:automationName': 'UiAutomator2', + 'appium:app': 'bs://', + 'appium:autoGrantPermissions': true, + 'bstack:options': { + realMobile: true, + interactiveDebugging: true, + projectName: 'WDIO App Automation Project', + buildName: 'Android Local Build', + sessionName: 'Search Wikipedia - Local Test Android' + } + }, + + capabilities: [ + { + 'bstack:options': { + deviceName: 'Google Pixel 6', + osVersion: '12' + } + } + ] +}; + +// Apply common caps +exports.config.capabilities.forEach(cap => { + for (const key in exports.config.commonCapabilities) { + if (typeof cap[key] === 'object') { + cap[key] = { ...exports.config.commonCapabilities[key], ...cap[key] }; + } else { + cap[key] = exports.config.commonCapabilities[key]; + } + } +}); diff --git a/custom_wdio_E2E/android/configs/wdio.browserstack.parallel.conf.js b/custom_wdio_E2E/android/configs/wdio.browserstack.parallel.conf.js new file mode 100644 index 0000000..9787889 --- /dev/null +++ b/custom_wdio_E2E/android/configs/wdio.browserstack.parallel.conf.js @@ -0,0 +1,67 @@ +const baseConfig = require('./wdio.base.conf'); + +exports.config = { + ...baseConfig.config, + + services: [ + [ + 'browserstack', + { buildIdentifier: '#${BUILD_NUMBER}' }, + ], + ], + + maxInstances: 3, + + commonCapabilities: { + platformName: 'android', + 'appium:automationName': 'UiAutomator2', + 'appium:app': 'bs://', + 'appium:autoGrantPermissions': true, + 'bstack:options': { + projectName: 'WDIO App Automation Project', + sessionName: 'Sample App - Public Test', + buildName: 'Android Parallel Build', + realMobile: true, + debug: true, + interactiveDebugging: true, + local: false + } + }, + + capabilities: [ + { + 'bstack:options': { + deviceName: 'Samsung Galaxy S22', + osVersion: '12.0', + sessionName: 'Parallel - S22' + } + }, + { + 'bstack:options': { + deviceName: 'Google Pixel 6', + osVersion: '12.0', + sessionName: 'Parallel - Pixel 6' + } + }, + { + 'bstack:options': { + deviceName: 'OnePlus 9', + osVersion: '11.0', + sessionName: 'Parallel - OnePlus 9' + } + } + ], + + +}; + +// Merge commonCapabilities into each capability +exports.config.capabilities.forEach(cap => { + for (const key in exports.config.commonCapabilities) { + if (typeof cap[key] === 'object') { + cap[key] = { ...exports.config.commonCapabilities[key], ...cap[key] }; + } else { + cap[key] = exports.config.commonCapabilities[key]; + } + } +}); diff --git a/custom_wdio_E2E/android/configs/wdio.browserstack.public.conf.js b/custom_wdio_E2E/android/configs/wdio.browserstack.public.conf.js new file mode 100644 index 0000000..368dd59 --- /dev/null +++ b/custom_wdio_E2E/android/configs/wdio.browserstack.public.conf.js @@ -0,0 +1,48 @@ +const baseConfig = require('./wdio.base.conf'); + +exports.config = { + ...baseConfig.config, + + services: [ + [ + 'browserstack', + { buildIdentifier: '#${BUILD_NUMBER}' }, + ], + ], + + commonCapabilities: { + platformName: 'android', + 'appium:automationName': 'UiAutomator2', + 'appium:app': 'bs://', + 'appium:autoGrantPermissions': true, + 'bstack:options': { + realMobile: true, + projectName: 'WDIO App Automation Project', + buildName: 'Android Build - Public App Test', + sessionName: 'Search Wikipedia - Public Test Android', + debug: true, + interactiveDebugging: true, + local: false + } + }, + + capabilities: [ + { + 'bstack:options': { + deviceName: 'Samsung Galaxy S22', + osVersion: '12' + } + } + ] +}; + +// Apply common caps +exports.config.capabilities.forEach(cap => { + for (const key in exports.config.commonCapabilities) { + if (typeof cap[key] === 'object') { + cap[key] = { ...exports.config.commonCapabilities[key], ...cap[key] }; + } else { + cap[key] = exports.config.commonCapabilities[key]; + } + } +}); diff --git a/custom_wdio_E2E/android/features/search.feature b/custom_wdio_E2E/android/features/search.feature new file mode 100644 index 0000000..b57c45a --- /dev/null +++ b/custom_wdio_E2E/android/features/search.feature @@ -0,0 +1,15 @@ +Feature: Wikipedia Search Testing + + Scenario: Search for BrowserStack + Given I launch the search screen + When I search with keyword "BrowserStack" + Then The search results should be listed + Then I clear the search field to prevent interaction with it + Then I click on the ImageButton after every search + + Scenario: Search for WebdriverIO + Given I launch the search screen + When I search with keyword "WebdriverIO" + Then The search results should be listed + Then I clear the search field to prevent interaction with it + Then I click on the ImageButton after every search diff --git a/custom_wdio_E2E/android/features/searchWeb.feature b/custom_wdio_E2E/android/features/searchWeb.feature new file mode 100644 index 0000000..e39e99c --- /dev/null +++ b/custom_wdio_E2E/android/features/searchWeb.feature @@ -0,0 +1,15 @@ +Feature: Wikipedia Search Testing - Web Testing Tools + + Scenario: Search for Playwright + Given I launch the search screen + When I search with keyword "Playwright" + Then The search results should be listed + Then I clear the search field to prevent interaction with it + Then I click on the ImageButton after every search + + Scenario: Search for Cypress + Given I launch the search screen + When I search with keyword "Cypress" + Then The search results should be listed + Then I clear the search field to prevent interaction with it + Then I click on the ImageButton after every search diff --git a/custom_wdio_E2E/android/package.json b/custom_wdio_E2E/android/package.json new file mode 100644 index 0000000..c7871b6 --- /dev/null +++ b/custom_wdio_E2E/android/package.json @@ -0,0 +1,31 @@ +{ + "name": "wdio_appaut_cucumber", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "npx wdio run wdio.conf.js", + "test:local": "npx wdio run ./configs/wdio.browserstack.local.conf.js", + "test:public": "npx wdio run ./configs/wdio.browserstack.public.conf.js", + "test:parallel": "npx wdio run ./configs/wdio.browserstack.parallel.conf.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@babel/register": "^7.25.9", + "@wdio/browserstack-service": "^9.12.5", + "@wdio/cli": "^9.12.5", + "@wdio/cucumber-framework": "^9.12.5", + "@wdio/local-runner": "^9.12.5", + "@wdio/selenium-standalone-service": "^8.14.0", + "@wdio/spec-reporter": "^9.12.3", + "appium-xcuitest-driver": "^9.2.0", + "chai": "^5.2.0", + "cucumber": "^6.0.7", + "wdio-chromedriver-service": "^8.1.1" + }, + "dependencies": { + "appium": "^2.18.0" + } +} diff --git a/custom_wdio_E2E/android/step-definitions/searchSteps.js b/custom_wdio_E2E/android/step-definitions/searchSteps.js new file mode 100644 index 0000000..9858c37 --- /dev/null +++ b/custom_wdio_E2E/android/step-definitions/searchSteps.js @@ -0,0 +1,59 @@ +// import { Given, When, Then } from '@wdio/cucumber-framework'; + +// Given('I try to search using Wikipedia App', async () => { +// const searchInit = await $('android=new UiSelector().resourceId("org.wikipedia.alpha:id/search_container")'); +// await searchInit.click(); +// }); + +// When('I search with keyword BrowserStack', async () => { +// const searchInput = await $('android=new UiSelector().resourceId("org.wikipedia.alpha:id/search_src_text")'); +// await searchInput.setValue('BrowserStack'); +// }); + +// Then('The search results should be listed', async () => { +// const results = await $$('android=new UiSelector().resourceId("org.wikipedia.alpha:id/page_list_item_title")'); +// await expect(results.length).toBeGreaterThan(0); +// }); + +import { Given, When, Then } from '@wdio/cucumber-framework'; + + +Given('I launch the search screen', async () => { + const searchContainer = await $('android=new UiSelector().resourceId("org.wikipedia.alpha:id/search_container")'); + + // If search container is not visible, navigate back twice + let retries = 0; + while (!(await searchContainer.isDisplayed()) && retries < 2) { + await driver.back(); + retries++; + } + + // Ensure the search container is displayed + await searchContainer.waitForDisplayed({ timeout: 5000 }); + await searchContainer.click(); + + const searchInput = await $('android=new UiSelector().resourceId("org.wikipedia.alpha:id/search_src_text")'); + await searchInput.waitForDisplayed({ timeout: 15000 }); + await searchInput.clearValue(); // Clear input field in case it's pre-filled +}); + +When(/^I search with keyword "(.*)"$/, async (searchTerm) => { + const searchInput = await $('android=new UiSelector().resourceId("org.wikipedia.alpha:id/search_src_text")'); + await searchInput.setValue(searchTerm); +}); + +Then('The search results should be listed', async () => { + const results = await $$('android=new UiSelector().resourceId("org.wikipedia.alpha:id/page_list_item_title")'); + await expect(results.length).toBeGreaterThan(0); +}); + +Then('I clear the search field to prevent interaction with it', async () => { + const searchInput = await $('android=new UiSelector().resourceId("org.wikipedia.alpha:id/search_src_text")'); + await searchInput.clearValue(); // Clear the search field to prevent further interaction +}); + +Then('I click on the ImageButton after every search', async () => { + // Find the ImageButton by its class name and click it after each search + const el4 = await $('android.widget.ImageButton'); // CLASS_NAME + await el4.click(); +}); diff --git a/custom_wdio_E2E/ios/app/BStackSampleApp.ipa b/custom_wdio_E2E/ios/app/BStackSampleApp.ipa new file mode 100644 index 0000000..c1891b8 Binary files /dev/null and b/custom_wdio_E2E/ios/app/BStackSampleApp.ipa differ diff --git a/custom_wdio_E2E/ios/configs/wdio.base.conf.js b/custom_wdio_E2E/ios/configs/wdio.base.conf.js new file mode 100644 index 0000000..911bac0 --- /dev/null +++ b/custom_wdio_E2E/ios/configs/wdio.base.conf.js @@ -0,0 +1,19 @@ +const path = require('path'); + +exports.config = { + user: process.env.BROWSERSTACK_USERNAME || 'YOUR_USERNAME', + key: process.env.BROWSERSTACK_ACCESS_KEY || 'YOUR_ACCESS_KEY', + + specs: [ + '../features/**/*.feature' // Adjust this to the correct feature file location + ], + + logLevel: 'info', + framework: 'cucumber', + + cucumberOpts: { + require: [path.resolve(__dirname, '../step-definitions/**/*.js')], + timeout: 60000 + } + }; + \ No newline at end of file diff --git a/custom_wdio_E2E/ios/configs/wdio.browserstack.local.conf.js b/custom_wdio_E2E/ios/configs/wdio.browserstack.local.conf.js new file mode 100644 index 0000000..13e4330 --- /dev/null +++ b/custom_wdio_E2E/ios/configs/wdio.browserstack.local.conf.js @@ -0,0 +1,50 @@ +const baseConfig = require('./wdio.base.conf'); + +exports.config = { + ...baseConfig.config, + services: [ + ['browserstack', + { + browserstackLocal: true, + buildIdentifier: '#${BUILD_NUMBER}', + opts: { + forcelocal: false, + localIdentifier: 'webdriverio-browserstack-repo4' + } + } + ], + ], + + + commonCapabilities: { + platformName: 'ios', + 'appium:app': 'bs://', + 'bstack:options': { + interactiveDebugging: true, + projectName: 'WDIO App Automation Project', + sessionName: 'Sample App - Public Test', + buildName: 'iOS Local Build', + sessionName: 'Search Wikipedia - Local Test iOS' + } + }, + + capabilities: [ + { + 'bstack:options': { + deviceName: 'iPhone 15', + osVersion: '17' + } + } + ] +}; + +// Apply common caps +exports.config.capabilities.forEach(cap => { + for (const key in exports.config.commonCapabilities) { + if (typeof cap[key] === 'object') { + cap[key] = { ...exports.config.commonCapabilities[key], ...cap[key] }; + } else { + cap[key] = exports.config.commonCapabilities[key]; + } + } +}); diff --git a/custom_wdio_E2E/ios/configs/wdio.browserstack.parallel.conf.js b/custom_wdio_E2E/ios/configs/wdio.browserstack.parallel.conf.js new file mode 100644 index 0000000..a0bf383 --- /dev/null +++ b/custom_wdio_E2E/ios/configs/wdio.browserstack.parallel.conf.js @@ -0,0 +1,63 @@ +const baseConfig = require('./wdio.base.conf'); + +exports.config = { + ...baseConfig.config, + + services: [ + [ + 'browserstack', + { buildIdentifier: '#${BUILD_NUMBER}' }, + ], + ], + + maxInstances: 3, + + commonCapabilities: { + platformName: 'ios', + 'appium:app': 'bs://', + 'appium:autoGrantPermissions': true, + 'bstack:options': { + projectName: 'WDIO App Automation Project', + buildName: 'iOS Parallel Build', + debug: true, + interactiveDebugging: true, + } + }, + + capabilities: [ + { + 'bstack:options': { + deviceName: 'iPhone 14', + osVersion: '16', + sessionName: 'Parallel - iPhone 14' + } + }, + { + 'bstack:options': { + deviceName: 'iPhone 16', + osVersion: '18', + sessionName: 'Parallel - iPhone 16' + } + }, + { + 'bstack:options': { + deviceName: 'iPhone 15', + osVersion: '17', + sessionName: 'Parallel - iPhone 15' + } + } + ], + + +}; + +// Merge commonCapabilities into each capability +exports.config.capabilities.forEach(cap => { + for (const key in exports.config.commonCapabilities) { + if (typeof cap[key] === 'object') { + cap[key] = { ...exports.config.commonCapabilities[key], ...cap[key] }; + } else { + cap[key] = exports.config.commonCapabilities[key]; + } + } +}); diff --git a/custom_wdio_E2E/ios/configs/wdio.browserstack.public.conf.js b/custom_wdio_E2E/ios/configs/wdio.browserstack.public.conf.js new file mode 100644 index 0000000..5886d2b --- /dev/null +++ b/custom_wdio_E2E/ios/configs/wdio.browserstack.public.conf.js @@ -0,0 +1,44 @@ +const baseConfig = require('./wdio.base.conf'); + +exports.config = { + ...baseConfig.config, + + services: [ + [ + 'browserstack', + { buildIdentifier: '#${BUILD_NUMBER}' }, + ], + ], + + commonCapabilities: { + platformName: 'ios', + 'appium:app': 'bs://', + 'bstack:options': { + projectName: 'WDIO App Automation Project IOS', + buildName: 'Public App Test iOS', + sessionName: 'Sample App - Public Test', + debug: true, + interactiveDebugging: true, + } + }, + + capabilities: [ + { + 'bstack:options': { + deviceName: 'iPhone 14', + osVersion: '16' + } + } + ] +}; + +// Apply common caps +exports.config.capabilities.forEach(cap => { + for (const key in exports.config.commonCapabilities) { + if (typeof cap[key] === 'object') { + cap[key] = { ...exports.config.commonCapabilities[key], ...cap[key] }; + } else { + cap[key] = exports.config.commonCapabilities[key]; + } + } +}); diff --git a/custom_wdio_E2E/ios/features/text-verification.feature b/custom_wdio_E2E/ios/features/text-verification.feature new file mode 100644 index 0000000..054e3d0 --- /dev/null +++ b/custom_wdio_E2E/ios/features/text-verification.feature @@ -0,0 +1,13 @@ +Feature: Text Verification + + Scenario: Verify input text matches displayed output - BrowserStack Email + Given I launch the app + When I click the "Text Button" + And I enter "hello@browserstack.com" into the input field + Then I should see "hello@browserstack.com" as the output text + + Scenario: Verify input text matches displayed output - Test Email + Given I launch the app + When I click the "Text Button" + And I enter "test@browserstack.com" into the input field + Then I should see "test@browserstack.com" as the output text diff --git a/custom_wdio_E2E/ios/features/text-verification2.feature b/custom_wdio_E2E/ios/features/text-verification2.feature new file mode 100644 index 0000000..062e70a --- /dev/null +++ b/custom_wdio_E2E/ios/features/text-verification2.feature @@ -0,0 +1,13 @@ +Feature: Text Verification - Return + + Scenario: Verify input text matches displayed output - BrowserStack Email + Given I launch the app + When I click the "Text Button" + And I enter "Hello" into the input field + Then I should see "Hello" as the output text + + Scenario: Verify input text matches displayed output - Test Email + Given I launch the app + When I click the "Text Button" + And I enter "This Works" into the input field + Then I should see "This Works" as the output text diff --git a/custom_wdio_E2E/ios/package.json b/custom_wdio_E2E/ios/package.json new file mode 100644 index 0000000..cd298b9 --- /dev/null +++ b/custom_wdio_E2E/ios/package.json @@ -0,0 +1,28 @@ +{ + "name": "wdio_appaut_cucumber_ios", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "npx wdio run wdio.conf.js", + "test:local": "npx wdio run ./configs/wdio.browserstack.local.conf.js", + "test:public": "npx wdio run ./configs/wdio.browserstack.public.conf.js", + "test:parallel": "npx wdio run ./configs/wdio.browserstack.parallel.conf.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@babel/register": "^7.25.9", + "@wdio/browserstack-service": "^9.12.5", + "@wdio/cli": "^9.12.5", + "@wdio/cucumber-framework": "^9.12.5", + "@wdio/local-runner": "^9.12.5", + "@wdio/selenium-standalone-service": "^8.14.0", + "@wdio/spec-reporter": "^9.12.3", + "appium-xcuitest-driver": "^9.2.0", + "chai": "^5.2.0", + "cucumber": "^6.0.7", + "wdio-chromedriver-service": "^8.1.1" + } +} diff --git a/custom_wdio_E2E/ios/step-definitions/textVerificationSteps.js b/custom_wdio_E2E/ios/step-definitions/textVerificationSteps.js new file mode 100644 index 0000000..70e0ac0 --- /dev/null +++ b/custom_wdio_E2E/ios/step-definitions/textVerificationSteps.js @@ -0,0 +1,34 @@ +const { Given, When, Then } = require('@wdio/cucumber-framework'); +const assert = require('assert'); + +Given('I launch the app', async () => { + const textButton = await $(`~Text Button`); + if (!(await textButton.isDisplayed())) { + let retries = 0; + while (!(await textButton.isDisplayed()) && retries < 2) { + await driver.back(); + retries++; + } + } +}); + + +When('I click the {string}', async (buttonName) => { + const button = await $(`~${buttonName}`); + await button.waitForDisplayed({ timeout: 30000 }); + await button.click(); +}); + +When('I enter {string} into the input field', async (inputText) => { + const inputField = await $(`~Text Input`); + await inputField.waitForDisplayed({ timeout: 30000 }); + await inputField.click(); + await inputField.addValue(inputText + "\n"); +}); + +Then('I should see {string} as the output text', async (expectedText) => { + const outputField = await $(`~Text Output`); + await outputField.waitForDisplayed({ timeout: 30000 }); + const actualText = await outputField.getText(); + assert.strictEqual(actualText, expectedText, `Expected "${expectedText}", but got "${actualText}"`); +});