diff --git a/Assert.js b/Assert.js index 23cb374..b1dc49c 100644 --- a/Assert.js +++ b/Assert.js @@ -1,27 +1,43 @@ -const env = require('./../../../wdio.env'); +import env from '../../../wdio.env.js' +const envObj = new env(); +const params = envObj.getParams(); -class Assert{ +export default class Asserts{ - pause() + async pauseIfHuman(seconds = params.is_human_pause) { - if(env.is_human) - { - browser.pause(env.is_human_pause*1000); + if(params.is_human) { + await browser.pause(seconds); } } - pageTitle(text) + async pageUrl(text) { - return expect(browser).toHaveTitleContaining(text); + return expect(browser).toHaveUrl(text); + } + async pageTitle(text) + { + return expect(browser).toHaveTitle(expect.stringContaining(text)); } - text(selector, text) + async text(selector, text) { - return expect(selector).toHaveTextContaining(text); + return expect(selector).toHaveText(expect.stringContaining(text)); } + async isDisplayed(selector) + { + return expect(selector).toBeDisplayed(); + } -}; + async isClickable(selector) + { + return expect(selector).toBeClickable(); + } -module.exports = new Assert() + async hasAttributeValue(selector, attribute, value) + { + return expect(selector).toHaveAttribute(attribute, expect.stringContaining(value)); + } +}; \ No newline at end of file diff --git a/Helper.js b/Helper.js new file mode 100644 index 0000000..521f7b3 --- /dev/null +++ b/Helper.js @@ -0,0 +1,67 @@ +export default class Helper{ + + async checkBaseUrlResponse(base_url){ // Method to check wether the base_url reponse is 200 or not + const response = await fetch(base_url); + expect(response.status).toBe(200); + } + + async waitForDisplayed(element, millisecond = 1000){ + try { + await element.waitForDisplayed({ timeout: millisecond }) + } catch (error) { + throw `Timeout: Element '${element.selector}' was not displayed after ${millisecond}ms. Original error: ${error.message}` + } + } + + async waitForAbsent(element, millisecond = 2000){ + await element.waitForDisplayed({ reverse: true, timeout: millisecond}); + } + + async scrollIntoView(element){ + await element.scrollIntoView({ block: 'center', inline: 'center' }); + } + + async scroll(x, y){ // Here the parameters 'x' & 'y' denotes the horizontal & vertical scroll values + await browser.scroll(x, y); + } + + async waitForClickable(element, millisecond = 2000){ + await element.waitForClickable({ timeout: millisecond }); + } + + async waitForEnabled(element, millisecond = 2000){ + await element.waitForEnabled({ timeout: millisecond }); + } + + async waitForDisabled(element, millisecond = 2000){ + await element.waitForEnabled({ reverse: true, timeout: millisecond }) + } + + async waitForExist(element, millisecond = 2000){ + await element.waitForExist({ timeout: millisecond }); + } + + async elementScreenshot(element, filename){ + await element.saveScreenshot(filename); + } + + async browserScreenshot(filename){ + await browser.saveScreenshot(filename); + } + + async switchFrame(iframe){ // Here the parameter 'iframe' is a valid selector for the iframe element + try { + await browser.switchToFrame(iframe); + } catch (error) { + throw `Failed to switch to the specified iframe (selector: '${iframe}'). Original error: ${error.message}` + } + } + + async switchWindow(handle){ // Here the parameter 'handle' can be a URL handle or page title + try { + await browser.switchToWindow(handle); + } catch (error) { + throw `Failed to switch to the specified window (handle: '${handle}'). Original error: ${error.message}` + } + } +} \ No newline at end of file diff --git a/Page.js b/Page.js index a50c7f6..3537cea 100644 --- a/Page.js +++ b/Page.js @@ -1,14 +1,26 @@ -const chalk = require('chalk'); -const env = require('./../../../wdio.env'); +import chalk from 'chalk'; +import color from 'cli-color' +import env from '../../../wdio.env.js' + +chalk.enabled = true +chalk.level = 3 + +const envObj = new env(); +const params = envObj.getParams(); + /** -* main page object containing all methods, selectors and functionality -* that is shared across all page objects -*/ -module.exports = class Page { + * main page object containing all methods, selectors and functionality + * that is shared across all page objects + */ +export default class Page { constructor() { - this.base_url = env.base_url; + this.base_url = params.base_url; + this.is_human_pause = params.is_human_pause; + this.small_pause = params.small_pause; + this.medium_pause = params.medium_pause; + this.long_pause = params.long_pause; this.params = { page: { id: null, @@ -35,6 +47,11 @@ module.exports = class Page { return browser.url(url); } //------------------------------------------------- + bold(str) + { + return chalk.bold(str); + } + //------------------------------------------------- highlight(str) { return chalk.magenta(str); @@ -42,15 +59,14 @@ module.exports = class Page { //------------------------------------------------- pageId(params) { - - return ` -[PAGE ID: ${this.highlight(params.page.id)}] Page: `+params.page.name+` URL: `+params.page.url; + return this.bold(` +[PAGE ID: ${this.highlight(params.page.id)}] Page: `+params.page.name+` URL: `+params.page.url); } //------------------------------------------------- groupId(params) { let id = chalk.red(` ----------------------------------------------------------------`); +--------------------------------------------------------------------------------------------------------`); id += this.pageId(params); id += ` [GROUP ID: `+this.highlight(params.page.id+"_"+params.group.count)+"] "+params.group.name; @@ -64,7 +80,7 @@ module.exports = class Page { if(params.test.expect) { id += ` - ${chalk.blue('Expect:')} ${params.test.expect}`; + ${color.blue('Expect:')} ${params.test.expect}`; } @@ -72,4 +88,4 @@ module.exports = class Page { } //------------------------------------------------- //------------------------------------------------- -} +} \ No newline at end of file diff --git a/README.md b/README.md index 994bae0..94ae2ac 100644 --- a/README.md +++ b/README.md @@ -8,52 +8,66 @@ Helpful classes to reduce code & accelerate speed for writing test cases for - Generate a well `formated` report ## Setup -##### Step 1: Clone or add as this repo as submodule to root of `webdriverio` tests folder with folder name `vaah-webdriverio` -Demo: https://img-v3.getdemo.dev/screenshot/spjG338m6A.mp4 +### Step 1: Clone or add as this repo as submodule to root of `webdriverio` tests folder with folder name `vaah-webdriverio` +Demo: https://drive.google.com/file/d/1GufgBWKTGBVkphCn5AFqheVTza75s3kl/view?usp=sharing -##### Step 2: Configure `wdio.env.sample.js` +### Step 2: Configure `wdio.env.sample.js` - Rename `wdio.env.sample.js` to `wdio.env.js` - Move `wdio.env.js` to the `root` folder of your project or where `wdio.conf.js` exist -Demo: https://img-v3.getdemo.dev/screenshot/HwjLwZEoOk.mp4 +Demo: https://drive.google.com/file/d/11F6IezeWfDT8elwfGhVFVsRP3ebtiMlE/view?usp=sharing -##### Step 3: Include `wdio.env.js` -In `wdio.conf.js`, include `wdio.env.js` and update following variables: +### Step 3: Include `wdio.env.js` +In `wdio.conf.js`, include `wdio.env.js` file and update the `env`, `baseUrl`, `logLevel` and `capabilities` variables: ```js -const env = require('./wdio.env'); +import env from "./wdio.env.js"; +const envObj = new env(); +const params = envObj.getParams(); -exports.config = { +export const config = { ... - env: env, - baseUrl: env.base_url, + env: params, + capabilities: params.capabilities, + logLevel: params.log_level, + baseUrl: params.base_url, ... } ``` -Demo: https://img-v3.getdemo.dev/screenshot/eNboGGqmrh.mp4 +Demo: https://drive.google.com/file/d/1kWrtqGTUbvYIAhAOAxpg7WJdZVQt229a/view?usp=sharing -In `env.js` tester should set the base URL based on their test environment. // Make sure that the URL ends with '/'. -````js -case 'localhost': - params.base_url = null // Instead of null insert your base URL inside " ". - break; -```` -Demo: https://img-v3.getdemo.dev/screenshot/BiI0D6ygq3.mp4 - +### Step 4: Updated `wdio.env.js` file +In `wdio.env.js` tester should set the base URL based on their test environment. // Make sure that the URL ends with '/'. +```js + this.params = { + debug: false, + is_human: true, + is_human_pause: 1000, + env: null, + log_level: 'error', + small_pause: 2000, + medium_pause: 5000, + long_pause: 10000, + base_url: '', // Instead of '', insert your base URL. + version: null, + capabilities: [ + ... +``` +Demo: https://drive.google.com/file/d/1eEVImCxkZWG5bBtDMcA27RRaw7dUZtao/view?usp=sharing -##### Step 4: Extend `pageobjects` and variables in `constructor` -Extend all your `pageobjects` to `const Page = require('./../vaah-webdriverio/Page');`, +### Step 5: Extend `pageobjects` and variables in `constructor` +Extend all your `pageobjects` to `import Page from '../vaah-webdriverio/Page.js';`, -Example: `pageobjects/Login.page.js`: +Example: For a pageobject file - `pageobjects/Login.page.js`, we have to import and extend Page.js file. ```js -const Page = require('./../vaah-webdriverio/Page'); +import Page from '../webdriverio-hepler/Page.js' -class Login extends Page { +export default class Login extends Page { constructor() { super(); @@ -70,49 +84,22 @@ class Login extends Page { } return super.open(this.page.url); } - + ... } -module.exports = new Login(); ``` -Demo: https://img-v3.getdemo.dev/screenshot/iTSi72u1p3.mp4 - -#### step 5: All the methods present in Assert class under Assets in vaah-webdriverio should be prefixed with "async" to run the methods in async mode in the test scripts. -Example: -```js -const env = require('./../../../wdio.env'); - -class Assert{ - - async pause() - { - if(env.is_human) - { - browser.pause(env.is_human_pause*1000); - } - } - - async pageTitle(text) - { - return expect(browser).toHaveTitleContaining(text); - - } - - async text(selector, text) { - await expect(selector).toHaveTextContaining(text); - await this.pause(); - } - -}; +Demo: https://drive.google.com/file/d/1qSu8nc99BJm9dp_M7Bn88orBg0vQT8Y8/view?usp=sharing -module.exports = new Assert() -```` -##### Step 6: Writing test cases -In `specs` folder create a file `login.e2e.js` and write following code for example: +### Step 6: Writing test cases +In `specs` folder create a file `login.spec.js` and write following code for example: ```js -const sl = require('../vaah-webdriverio/Selector'); -const assert = require('../vaah-webdriverio/Assert'); -const login = require('../pageobjects/login.page'); +import Selector from '../../vaah-webdriverio/Selector.js' +import Assert from '../../vaah-webdriverio/Assert.js' +import LoginPage from '../pageobjects/login.page.js' + +let Sl = new Selector(); +let asserts = new Assert(); +let login = new LoginPage(); login.group.count = 1; // Group counter which will be used to generate Group ID login.group.name = 'Login'; @@ -130,20 +117,22 @@ describe(login.groupId(), () => { login.open(); browser.maximizeWindow(); await assert.pageTitle("The Internet"); - sl.name("username", "tomsmith"); + Sl.name("username", "tomsmith"); // This will select the element with attribute as name='username' and will also insert the value "tomsmith". - sl.name("password", "SuperSecretPassword!"); - sl.class('radius').click(); - await assert.text(sl.id('flash'), login.test.data); + Sl.name("password", "SuperSecretPassword!"); + Sl.class('radius').click(); + await asserts.text(Sl.id('flash'), login.test.data); }); //----------------------------------------------------------- }); -```` -Demo: https://img-v3.getdemo.dev/screenshot/OdRIb4yXIr.mp4 +``` +Demo: https://drive.google.com/file/d/1auagekvKGp-Oghx8o-zUV7nFOU0BtUSJ/view?usp=sharing Note: This is just an example of where to write the test script. The test script may differ. +**To know more about the type of selectors used in the above example script, kindly refer to the table added below:** + | Selector | In Selector.js|Use|Description| |--|--|--|--| @@ -160,7 +149,7 @@ Note: This is just an example of where to write the test script. The test script Page object model will help you to store the element's attribute value at one place so that if there is a change in the value then we have to change it at one page rather then changing it at every instance. -To implement page object we need to to create a file to store these values. Inside the tests folder go to wdio folder and then go inside data folder (if the folder does not exist you can create one). Then inside the data folder create a javascript file elements.js and paste the below mentioned code. +To implement page object we need to create a file to store these values. Inside the tests folder go to wdio folder and then go inside data folder (if the folder does not exist you can create one). Then inside the data folder create a javascript file elements.js and paste the below mentioned code. Demo: https://img-v3.getdemo.dev/screenshot/37DZHpTEcH.mp4 @@ -309,7 +298,7 @@ describe(page.groupId(params), () => { ``` Demo: https://img-v4.getdemo.dev/screenshot/phpstorm64_KzTsODht7l.mp4 -##### Step 7: Run test +#### Step 7: Run test Now, you can run the test via: ```sh npx wdio --spec ./tests/wdio/specs/login.e2e.js @@ -326,7 +315,7 @@ Demo: https://img-v3.getdemo.dev/screenshot/AWcVR496IG.mp4 The Demo shows how a passed and failed test cases will be represented. -##### Step 8: Result +#### Step 8: Result @@ -361,7 +350,7 @@ or you can even run the test cases based on a specific keyword: e.g. npx wdio --mochaOpts.grep smoke Demo: https://img-v3.getdemo.dev/screenshot/vju9IYLTiO.mp4 ``` -##### Possible error +#### Possible error ``` @wdio/runner: Error: Failed to create session. session not created: This version of ChromeDriver only supports Chrome version 96 @@ -379,4 +368,4 @@ Demo: https://img-v3.getdemo.dev/screenshot/xL5B0R3Gar.mp4 Please consider starring the project to show your :heart: and support. -[WebReinvent](https://webreinvent.com) is a web agency based in Delhi, India. You'll find an overview of all our open source projects [on github](https://github.com/webreinvent). +[WebReinvent](https://webreinvent.com) is a web agency based in Delhi, India. You'll find an overview of all our open source projects [on github](https://github.com/webreinvent). \ No newline at end of file diff --git a/Selector.js b/Selector.js index 698ee0a..2d831ce 100644 --- a/Selector.js +++ b/Selector.js @@ -1,4 +1,4 @@ -class El{ +export default class Selector{ //----------------------------------------------------- id(id) @@ -71,7 +71,50 @@ class El{ return el; } //----------------------------------------------------- - -} - -module.exports = new El() \ No newline at end of file + arialabel(name, value=null) + { + let el = this.attr('aria-label', name); + if(value) + { + el.setValue(value) + } + return el; + } + //----------------------------------------------------- + placeholder(name, value=null){ + let el = this.attr('placeholder', name); + if(value) + { + el.setValue(value) + } + return el; + } + //----------------------------------------------------- + title(name, value=null){ + let el = this.attr('title', name); + if(value) + { + el.setValue(value) + } + return el; + } + //----------------------------------------------------- + href(name, value=null){ + let el = this.attr('href', name); + if(value) + { + el.setValue(value) + } + return el; + } + //----------------------------------------------------- + value(name, value=null){ + let el = this.attr('value', name); + if(value) + { + el.setValue(value) + } + return el; + } + //----------------------------------------------------- +} \ No newline at end of file diff --git a/wdio.env.sample.js b/wdio.env.sample.js index 3606aa5..17ca3f2 100644 --- a/wdio.env.sample.js +++ b/wdio.env.sample.js @@ -1,44 +1,130 @@ -let params = { - debug: true, - is_human: false, - is_human_pause: 2, // in seconds - env: null, - base_url: null, - version: null, -}; -/* -|-------------------------------------------------------------------------- -| Site Environment -|-------------------------------------------------------------------------- -*/ -params.env = 'localhost'; -//params.env = 'develop'; -//params.env = 'staging'; -//params.env = 'production'; - -switch(params.env) -{ - case 'localhost': - params.base_url = null - break; - - case 'develop': - params.base_url = null - break; - - case 'staging': - params.base_url = null - break; - - case 'production': - params.base_url = null - break; - - default: - params.base_url = null - break; +class Env { -} + constructor() { + + this.params = { + debug: false, + is_human: true, + is_human_pause: 1000, + env: null, + log_level: 'error', + small_pause: 2000, + medium_pause: 5000, + long_pause: 10000, + base_url: '', + version: null, + capabilities: [ + { + platformName: "mac", + "appium:automationName": "Chromium", + browserName: 'chrome', + acceptInsecureCerts: true, + }, + ] + }; + } + + //------------------------------------------------- + setCapabilities() + { + + switch(process.env.NODE_WDIO_OS) + { + case 'mac': + this.params.capabilities = [ + { + platformName: "mac", + "appium:automationName": "Chromium", + browserName: 'chrome', + acceptInsecureCerts: true, + }, { + platformName: "mac", + "appium:automationName": "Safari", + browserName: 'Safari' + } + ] + break; + case 'ios': + this.params.capabilities = [ + { + platformName: 'iOS', + browserName: 'Safari', + 'appium:platformVersion': '17.0', + 'appium:automationName': 'XCUITest' + } + ] + break; + case 'android': + this.params.capabilities = [ + { + platformName: 'Android', + browserName: 'Chrome', + acceptInsecureCerts: true, + "appium:deviceName": "Pixel_3a_API_34", //this should match with emulator name + 'appium:automationName': 'UIAutomator2', + } + ] + break; + case 'windows': + this.params.capabilities = [ + { + platformName: "Window", + "appium:automationName": "Chromium", + browserName: 'chrome', + acceptInsecureCerts: true, + } + ] + break; + case 'linux': + this.params.capabilities = [ + { + platformName: "Linux", + "appium:automationName": "Chromium", + browserName: 'chrome', + acceptInsecureCerts: true, + } + ] + break; + } + + } + //------------------------------------------------- + getCapabilities(){ + let capabilities = this.params.capabilities; + if(this.params.is_human === false){ + for(let i in capabilities) + { + switch(capabilities[i].browserName) + { + case 'chrome': + capabilities[i]['goog:chromeOptions'] = { + args: [ + '--no-sandbox', + '--disable-dev-shm-usage', + '--disable-infobars', + '--headless', + '--disable-gpu', + '--window-size=1440,735', + '--disable-extensions' + ] + } + break; + } + } + } + + return capabilities; + } + //------------------------------------------------- + getParams () { + this.params.capabilities = this.getCapabilities(); + + console.log('params-->', this.params); + + return this.params; + } + +} -module.exports = params; \ No newline at end of file +export default Env;