Mobile Test Automation using WebDriver.io and Appium

Mobile Test Automation using WebDriver.io and Appium

Benefits of Mobile Automation Testing:

  • Faster App Deployment: Manual testing can be time-consuming and resource-intensive when it comes to identifying bugs in code. Mobile automation testing reduces the time and effort required by using automation tools. These tools enable quick checks to ensure the code performs as desired, leading to a faster feedback loop and accelerated app deployment.

  • Improved Efficiency: With automated testing, there is no need for human intervention. Tests can be scheduled to run overnight, and results can be collected the next morning. By automating the testing process, app developers and QA teams can spend less time on testing and focus more on other critical tasks. This boosts overall productivity and efficiency.

  • Precision in Testing: Mobile automation testing is more reliable than manual testing, which is prone to human errors. Automated tests produce precise and consistent results, reducing the likelihood of bugs. Test cases generated by automated systems enhance reliability and minimize the probability of errors when launching the app.

  • Real-time Feedback: Automated testing provides instant feedback. Tests run quickly, and test reports are generated immediately. This enables developers to promptly address any malfunctions or issues detected during the testing process. Real-time feedback helps in identifying and resolving problems swiftly, ensuring app quality.

  • Cost Savings: Contrary to common misconception, automated testing is more cost-effective than manual testing. Manual testing can be repetitive and monotonous, leading to increased chances of human error. Automation increases testing speed and accuracy, reducing the need for extensive manual intervention. Once the automated testing scripts are developed, they can be reused 24/7 without incurring additional costs.

What is WebDriver.io?

WebdriverIO is an open-source testing automation framework written in JavaScript and running on NodeJS. Next-gen browser and mobile automation test framework for Node.js

Why Webdriver.IO?

WebdriverIO is a progressive automation framework built to automate modern web and mobile applications. It simplifies the interaction with your app and provides a set of plugins that help you create a scalable, robust, and stable test suite.

Features of WebDriver.io:

Easy to Set up: WebdriverIO follows a simple setup process. Just install node packages using npm and start testing

Customization: WebdriverIO is highly extendable so users can customize the framework as they need

Cross-Browser Testing: WebdriverIO supports multiple browsers such as Chrome, Edge, Firefox, Internet Explorer, and Safari.

Native Mobile Application Testing: WebdriverIO framework can be extended to test native mobile applications.

Multiple Tab/Window Support: WebdriverIO Supports switching to and from various windows and tabs.

iFrame Support: WebdriverIO doesn’t restrict in terms of iFrame. Testers can automate iframe-based scenarios using simple web driver commands.

Reporters: WebdriverIO supports more than dozens of reporters.

Testing Framework/Assertions: WebdriverIO supports Mocha, Jasmine, and Cucumber test frameworks.

Parallel Testing: Testers can configure WebdriverIO to launch multiple instances and execute tests parallelly.

Screenshots: WebdriverIO can be configured to take screenshots for tests.

Video: Though WebdriverIO doesn’t support video recording out of the box it can be configured to do so.

Pipeline Integration: WebdriverIO tests can be integrated into CI Systems like Jenkins, Azure, etc.

Selectors: It supports various types of selectors including CSS and Xpath.

Page Object Pattern: WebdriverIO Framework can be easily configured to Page Object Model.

File Upload and Download: WebdriverIO supports File Upload and Download features.

Mobile Automation with WebDriver.io: This enables code usage between iOS, Android, and Windows test suites. It runs on iOS and Android applications using the WebDriver protocol.

Technology stack: WebDriver.io, TypeScript, Appium, Android Studio, Cucumber, Node js, Browser Stack 

Requirement For Mobile Automation using webdriver.io:

Pre-Setup 

Installation & Configuration

Install the latest stable version of Android Studio from https://developer.android.com/studio

Then Install android-platform-tools from CLI

Install JDK’s latest stable version from here https://www.oracle.com/java/technologies/javase-jdk16-downloads.html

Download the latest stable version of VS Code from https://code.visualstudio.com/download 

Install the latest version of Allure for Report Generation from https://docs.qameta.io/allure/

Download and install the latest LTS Node.js – https://nodejs.org/en/download

Install Following Dependencies 

Commands to install the dependencies

npm i @wdio/allure-reporter
npm i @wdio/appium-service
npm i @wdio/browserstack-service
npm i @wdio/cli
npm i @wdio/cucumber-framework
npm i @wdio/local-runner
npm i @wdio/mocha-framework
npm i chromedriver
npm i wdio-chromedriver-service
npm i ts-node
npm i webdriverio
npm i allure-commandline
npm i appium

Official website links:

https://webdriver.io/

https://appium.io/

https://cucumber.io/

your packages will look like in package.json

"devDependencies": {
    "@wdio/allure-reporter": "^8.0.13",
    "@wdio/appium-service": "^8.0.13",
    "@wdio/browserstack-service": "^8.0.11",
    "@wdio/cli": "^8.0.13",
    "@wdio/cucumber-framework": "^8.0.13",
    "@wdio/local-runner": "^8.0.13",
    "@wdio/mocha-framework": "^8.0.13",
    "@wdio/spec-reporter": "^8.0.13",
    "chromedriver": "^108.0.0",
    "ts-node": "^10.9.1",
    "typescript": "^4.9.4",
    "wdio-chromedriver-service": "^8.0.1",
    "webdriverio": "^8.0.13"
  },
  "dependencies": {
    "allure-commandline": "^2.20.1",
    "appium": "^1.22.3"
  }

Use of Config file in wedriver.io:

In WebDriver.io, the config file (wdio.conf.js) is a crucial component used for configuring and customizing the test execution environment. It allows you to define various settings, options, and capabilities for your WebDriver.io tests. The config file acts as a central configuration hub for your test suites and provides flexibility in managing different test environments and setups.

Here are some common uses of the config file in WebDriver.io:

Specifying Test Framework and Reporter:

You can define the test framework (e.g., Mocha, Jasmine) and the reporter (e.g., Spec, Dot, Allure) for your test runs. This ensures that the tests are executed using the desired framework and provides appropriate reporting formats.

Defining Test Files and Suites:

You can specify the test files or directories containing the test files to be executed during a test run. Additionally, you can define test suites, which allow you to group related tests together for organized execution.

Configuring Test Environments:

The config file allows you to configure different test environments (e.g., local, remote, cloud-based) and set the desired capabilities for each environment. This includes specifying the browser or device to be used, browser version, operating system, and other relevant configurations.

Managing Selenium Grid and WebDriver Services:

If you’re using a Selenium Grid or WebDriver service, the config file lets you configure the connection details and capabilities for these services. You can specify the host, port, and other relevant configurations for connecting to the grid or service.

Defining Hooks and Lifecycle Events:

WebDriver.io provides hooks and lifecycle events that allow you to execute code at specific points during the test execution cycle. The config file lets you define these hooks, such as before and after hooks, to perform setup and teardown actions or customize test behavior.

Setting Timeout and Retry Options:

You can configure timeout values for various actions, such as test execution, page loading, and element interactions. Additionally, you can define retry options, which specify the number of times a test should be retried in case of failures.

Integrating with Test Services and Frameworks:

WebDriver.io supports integration with various test services and frameworks, such as Appium, Sauce Labs, and Cucumber. The config file allows you to set up and configure these integrations, enabling seamless usage and interaction with these services.

By utilizing the config file effectively, you can streamline and tailor your WebDriver.io test runs according to your specific requirements. It provides a flexible and centralized approach to managing test configurations, environments, and other essential aspects of your test automation setup.

Prerequisites for the Android Studio:

To use Android Studio, you will need to ensure that your system meets the following prerequisites:

Operating System:

Android Studio is compatible with Windows, macOS, and Linux operating systems. Make sure you have a supported version of the operating system installed on your computer.

Java Development Kit (JDK):

Android Studio requires a compatible version of the JDK to be installed. It is recommended to use the latest stable version of JDK. Currently, Android Studio supports JDK 8 or higher. You can download the JDK from the Oracle website or use OpenJDK.

System Requirements:

Android Studio has certain hardware requirements to function optimally. The exact specifications may vary depending on the version and updates of Android Studio, but generally, you should have:

Android SDK:

Android Studio requires the Android SDK (Software Development Kit) to develop and test Android applications. The SDK provides libraries, APIs, and tools necessary for app development. Android Studio includes a bundled version of the Android SDK, which you can install during the Android Studio installation process. Alternatively, you can download the SDK separately and configure Android Studio to use it.

Emulator Requirements:

If you plan to test your apps on emulated devices, your system should meet the requirements for running the Android Emulator. This includes having an Intel or AMD processor with virtualization extensions enabled in the BIOS settings.

Internet Connection:

An internet connection is required during the installation and setup process of Android Studio. It is necessary to download additional components, SDK packages, and updates. It’s important to note that the specific requirements and recommendations may change with different versions of Android Studio. It’s always a good practice to refer to the official documentation and system requirements provided by Google for the most up-to-date information before installing Android Studio.

What is use tsconfig.json:

The tsconfig.json file is used in TypeScript projects to configure the TypeScript compiler (tsc) and specify the compilation settings for your TypeScript code. It provides a way to customize the behavior of the TypeScript compiler and control how your TypeScript files are transpiled into JavaScript.

key uses and features of the tsconfig.json file:

Compiler Options:

The tsconfig.json file allows you to define various compiler options to specify how the TypeScript compiler should handle your code. These options include target ECMAScript version, module system (e.g., CommonJS, ES modules), output directory, source map generation, strictness level, and more. You can tailor these options to match the requirements of your project and the desired JavaScript output.

File Inclusion and Exclusion:

Using the include and exclude properties in tsconfig.json, you can specify the files or directories to include or exclude from the TypeScript compilation process. This helps you define the scope of compilation and avoid unnecessary collection of files that are not part of your project.

Module Resolution:

TypeScript supports different module resolution strategies (e.g., Node.js, Classic) for resolving module imports in your code. The tsconfig.json file allows you to specify the desired module resolution strategy using the module resolution compiler option.

Type Checking and Error Reporting:

TypeScript provides powerful type-checking capabilities. The tsconfig.json file enables you to configure the level of type-checking strictness using options like strict, noImplicitAny, strictNullChecks, and more. By adjusting these options, you can control the rigor of type checking and the level of error reporting during compilation.

Project References:

With the tsconfig.json file, you can set up project references, which enables you to organize your TypeScript code into multiple smaller projects and manage their dependencies. Project references allow you to compile and reference TypeScript code across projects and enforce dependencies between them.

Extended Configuration Inheritance:

The tsconfig.json file supports extending and inheriting configurations from base configuration files using the extends property. This allows you to define a common set of compiler options in a base configuration file and override or extend them in specific project configurations.

IDE and Tooling Integration:

IDEs and development tools, such as Visual Studio Code and other TypeScript-aware editors, use the tsconfig.json file to provide features like IntelliSense, code navigation, error highlighting, and build integration. The configuration file helps tools understand the project structure, dependencies, and compilation settings, enhancing the development experience.

The tsconfig.json file serves as a central configuration file for TypeScript projects, enabling you to customize and fine-tune the TypeScript compilation process according to the needs of your project. It helps maintain consistency, improves code quality, and ensures smooth integration with development tools and workflows.

In Behavior-Driven Development (BDD), feature files, step files, and page files are essential components used to describe and implement the behavior of a software system. BDD focuses on collaboration and communication between developers, testers, and stakeholders by using a common language that is easily understandable by all parties involved.

Feature file Step Definition and Page object file:

Feature Files:

A feature file is a textual representation of a software feature or functionality. It typically describes the behavior of a specific feature from a user’s perspective. Feature files are written using a plain-text format, often in the Gherkin syntax. They are used to capture high-level scenarios, user stories, and acceptance criteria in a structured manner. Feature files serve as a communication tool between stakeholders, developers, and testers to ensure a shared understanding of the system’s behavior.

Step Files:

Step files, also known as step definitions or step implementations, are code files that provide the actual implementation of the steps described in the feature files. Each step in a feature file is associated with a corresponding step definition in the step files. Step files are written in a programming language (such as JavaScript, Java, Ruby, etc.) and contain the logic and actions to be performed for each step. They connect the feature files with the underlying codebase and define the behavior of the system in response to the steps described in the feature files.

Page Files:

In the context of BDD test automation, page files represent the Page Object Model (POM) or similar abstraction that represents the user interface (UI) elements and actions of a web application. Page files provide a structured way to define and manage the UI elements, such as buttons, forms, input fields, etc., along with associated methods for interacting with those elements. They encapsulate the UI interactions and provide reusable methods to perform actions on the web application’s pages. Page files enhance the maintainability and reusability of the test automation code.

In summary, feature files describe the behavior of a software feature in a structured, human-readable format. Step files provide the implementation of the steps described in the feature files, connecting them to the underlying codebase. Page files, specific to BDD test automation, represent a web application’s UI elements and actions, encapsulating the UI interactions and providing reusable methods. These files collectively enable collaboration, communication, and automated testing in a BDD approach.

Let’s See about Appium 

‍Appium is an open-source tool for traditional automation, web, and hybrid apps on iOS, Android, and Windows desktop mobile platforms. Indigenous apps are those written using iOS and Android. Mobile web applications are accessed using a mobile browser (Appium supports Safari for iOS apps and Chrome or the built-in ‘Browser’ for Android apps). Hybrid apps have a wrapper around “web view”—a traditional controller that allows you to interact with web content. Projects like Apache Cordova make it easy to build applications using web technology and integrate them into a traditional wrapper, creating a hybrid application.

Importantly, Appium is “cross-platform”, allowing you to write tests against multiple platforms (iOS, Android), using the same API. This enables code usage between iOS, Android, and Windows test suites. It runs on iOS and Android applications using the WebDriver protocol.

Cucumber:

Cucumber is a tool for running automated tests written in plain language. Because they’re written in plain language, they can be read by anyone on your team. Because they can be read by anyone, you can use them to help improve communication, collaboration and trust in your team.

Android Emulator:

The Android Emulator simulates Android devices on your computer so that you can test your application on a variety of devices and Android API levels without needing to have each physical device. The emulator offers these advantages:

Flexibility: In addition to being able to simulate a variety of devices and Android API levels, the emulator comes with predefined configurations for various Android phones, tablets, Wear OS, and Android TV devices.

High fidelity: The emulator provides almost all the capabilities of a real Android device. You can simulate incoming phone calls and text messages, specify the location of the device, simulate different network speeds, simulate rotation and other hardware sensors, access the Google Play Store, and much more.

Speed: Testing your app on the emulator is in some ways faster and easier than doing so on a physical device. For example, you can transfer data faster to the emulator than to a device connected over USB.

In most cases, the emulator is the best option for your testing needs. This page covers the core emulator functionalities and how to get started with it.

Browser Stack:

BrowserStack is a cross-platform web browser testing tool that allows users to test their websites and mobile applications on different browsers and operating systems. It is available as a cloud-based service or as an on-premise solution. BrowserStack provides a range of features, including live testing, automated screenshots, and performance analysis. It is also compatible with a number of popular testing frameworks, such as Selenium, WebDriver, and Protractor. BrowserStack is a paid service, but it offers a free trial for new users.

Create a sample Appium Project:

initialize a project and create the package. json file

npm  init -y

Install Dependencies

npm  install

Install Webdriver.io

npm install -g webdriverio or npm install --save-dev webdriverio @wdio/cli

Generate config file

npx wdio config

This is how package.json will look:

{
  "name": "wdioappium",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "appium --address 127.0.0.1 --port 4723",
    "wdio": "wdio run ./wdio.conf.ts",
    "allure": "allure generate ./allure-results"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@wdio/allure-reporter": "^8.0.13",
    "@wdio/appium-service": "^8.0.13",
    "@wdio/browserstack-service": "^8.0.11",
    "@wdio/cli": "^8.0.13",
    "@wdio/cucumber-framework": "^8.0.13",
    "@wdio/local-runner": "^8.0.13",
    "@wdio/mocha-framework": "^8.0.13",
    "@wdio/spec-reporter": "^8.0.13",
    "chromedriver": "^108.0.0",
    "ts-node": "^10.9.1",
    "typescript": "^4.9.4",
    "wdio-chromedriver-service": "^8.0.1",
    "webdriverio": "^8.0.13"
  },
  "dependencies": {
    "allure-commandline": "^2.20.1",
    "appium": "^1.22.3"
  }
}

This is how your Configuration file will look like:

import type { Options } from '@wdio/types'
export const config: Options.Testrunner = {
  user: 'x',
  key: 'x',
  autoCompileOpts: {
    tsNodeOpts: {
      project: './tsconfig.json',
    },
  },
  specs: ['./Features/Demo.feature'],
  exclude: [
  ],
  maxInstances: 1,
  services: [
    [
      'appium',
      {
        command: 'appium',
        args: {
          relaxedSecurity: true,
          address: '127.0.0.1/wd/hub',
          log: './appium.log',
        },
      },
    ],
  ],
  services: [
    [
      'browserstack',
      {
       app: 'C:/Users/Admin/harish2/android/calculator.apk',
        // OR
      },
    ],
  ],
  capabilities: [
    {
      path: '/wd/hub',
      'appium:automationName': 'UiAutomator2',
      platformName: 'Android',
      'appium:platformVersion': '9.0',
      'appium:deviceName': 'Google Pixel 3',
      'appium:app': 'C:/Users/Admin/harish2/android/calculator.apk',
      'appium:udid': 'emulator-5554',
    },
  ],
   capabilities: [
     {
       project: 'First Webdriverio Android Project',
       build: 'browserstack-build-1',
       name: 'local_test',
       device: 'Google Pixel 3',
       os_version: '9.0',
       app: 'bs://643870ef70d1d507902e6844d9669388060beac7',
       'browserstack.local': true,
   },
   ],
  ...
  logLevel: 'info',
  framework: 'cucumber',
  // ...
  reporters: [
    [
      'allure',
      {
        outputDir: 'allure-results',
        disableWebdriverStepsReporting: true,
        disableWebdriverScreenshotsReporting: true,
      },
    ],
  ],
  cucumberOpts: {
    require: ['./Features/step-definitions/calc.ts', '*.js'],
  },
}

Step-by-Step Configuration of Android Emulator using Android Studio:

Launch android studio 

Step-by-Step Configuration of Android Emulator using Android Studio
Android Studio

Create / Select a Device 

And Click on the launch 

Appium Desktop configuration

Appium Desktop Configuration

Launch 

Appium

How to Write BDD test

Add services in the wdio.conf.ts file 
For Browser Stack :

services: [
    [
     'browserstack',
      {
        app: 'android/calculator.apk',
        // OR
      },
    ],
  ],

Add Capabilities in the wdio.conf.ts file:

capabilities: [
    {
      path: '/wd/hub',
      'appium:automationName': 'UiAutomator2',
      platformName: 'Android',
      'appium:platformVersion': '9.0',
      'appium:deviceName': 'Google Pixel 3',
      'appium:app': 'calculator.apk',
      'appium:udid': 'emulator-5554',
    },
  ],


Services for local Appium

services: [
    [
      'appium',
      {
        // This will use the globally installed version of Appium
        command: 'appium',
        args: {
          // This is needed to tell Appium that we can execute local ADB commands
          // and to automatically download the latest version of ChromeDrive
          relaxedSecurity: true,
          address: '127.0.0.1/wd/hub',
          // Write the Appium logs to a file in the root of the directory
          log: './appium.log',
        },
      },
    ],
  ],

Install cucumber 

$ npm install @cucumber/cucumber

Write your Feature File then, write your feature in features/calculator.feature

Feature: Mobile App Feature
Scenario: Verify user can perform addition operation on calculator
When User taps on fou
And User taps on +
And User taps on four
And User taps on equals
Then User Verify total is 8

Create a Step definition features/support/calc.ts

const { When, Then } = require('@cucumber/cucumber')
import { DemoPage } from '../page/DemoPage'
let Demo = new DemoPage()
When('User taps on four', async () => {
  await Demo.firstNumber()
})
When('User taps on +', async () => {
  await Demo.Add()
})
When('User taps on equals', async () => {
  await Demo.Equals()
})
Then('User Verify total is 8', async () => {
  await Demo.Verify()
})

Here Create a page file calculator.ts

export class DemoPage {
  async firstNumber() {
    await $('//android.widget.Button[@text="4"]').click()
  }
  async Add() {
    await $('//android.widget.Button[@text="+"]').click()
  }
  async Equals() {
    await $('//android.widget.Button[@text="="]').click()
  }
  async Verify() {
    const text = await $('//android.widget.TextView[@text="8"]').getText()
    expect(text).toEqual('8')
  }

Create a tsconfig.json

{
  "compilerOptions": {
    "moduleResolution": "node",
    "module": "CommonJS",
    "types": [
      "node",
      "@wdio/globals/types",
      "expect-webdriverio",
      "@wdio/mocha-framework",
      "@wdio/appium-service"
    ],
    "target": "ES2015"
  }
}

Create a folder apk and paste the .apk file in it. Execute the following command in terminal

"scripts": {
    "test": "appium --address 127.0.0.1 --port 4723",
    "wdio": "wdio run ./wdio.conf.ts",
  },

Run the test

npm  run test
npm run wdio

Results :

Allure Results:

Allure Report

Conclusion:

Webdriver.io has great support for Mobile automation and using Webriver.io users can Quickly set up the Mobile automation framework.BDD helps Teams across Organizations to work collaboratively and helps non-tech teams to understand the flow of applications. Tools like Appium make mobile automation easy. combination of Webdriver.io cucumber appium and BrowserStack makes things more. Reliable and useful in modern Mobile automation testing.

Read more blogs here

How to Automate tests using Taiko with Cucumber in JavaScript

How to Automate tests using Taiko with Cucumber in JavaScript

Hello! In this blog, I will be exploring how to automate tests using Taiko with Cucumber in JavaScript. The Taiko tool is easy to automate and is very reliable, and it works faster to execute and run test cases. It is a user-friendly tool as well. 

What is Takio?

A Taiko is an automation tool that is available for free and it is an open-source browser automation tool. It is built by the ThoughtWorks team. It uses the Node.js library to automate the chrome browser. Taiko is very useful to create maintainable and highly readable JavaScript tests.  

Taiko Features:

The Taiko was explicitly built to test modern web applications. 

The features of Taiko that set it apart from other browser automation solutions are listed below.

  1. Easy Installation
  2. Interactive Recorder
  3. Smart Selectors
  4. Handle XHR and dynamic content
  5. Request/Response stubbing and mocking

We can use Taiko on three platforms:

  1. Windows
  2. macOS
  3. Linux

How to install Taiko?

A Taiko is available on npm: You can use the following NPM command to install the taiko on your system.

npm install -g taiko

What is cucumber?

A Cucumber is a testing tool that allows BDD.  It offers a way to write tests that everyone, regardless of technical ability, can follow. Before developers build their code in BDD, users (business analysts, product owners) first write scenarios or acceptance tests that describe the system behavior from the perspective of the customer. Such scenarios and acceptance tests then are reviewed and approved by the product owners.

How to install Cucumber?

Basically, cucumber is available on npm: You can use the following NPM command to install the cucumber on your system.

npm install --save-dev @cucumber/cucumber

Getting Started 

We will be using Visual Studio code to write our test automation code in JavaScript. We will create a feature file first, then click on the left side of the panel and choose “new file” from the menu that appears. Give a file name after that, such as the Calculator. feature

I’ll start out by introducing the Taiko framework, which integrates BDD and Cucumber. You will be guided through the code in the next step.

Feature: Calculator operations
  @smoke
  Scenario: Addition of 2 numbers
    Given I launch calculator application
    When I click on number 2
    And I click on operator +
    And I click on number 2
    Then I verify the result is 4

I’ll describe how to automate the calculator page in this place. The code shown below builds calculator steps where we must import statements provided by cucumber before navigating to the support folder. 

After that, we can import the cucumber and assertion statements and that will build a page where all the steps are generally placed. 

Basically, this is the step definition file where we need to map the feature file steps and call methods declared in the page file.

After that, we have to import the page file in the step definition file.  And we need to call the methods declared in the page file. 

Following is the code snippet for the step definition file.

const { Given, When, Then } = require('@cucumber/cucumber')
const calculate = require('../../pages/calculatorPage')
Given('I launch calculator application', async function () {
  await new calculate().launch()
})
When('I click on number {string}', async function (num) {
  await new calculate().click_number(num)
})
When('I click on operator {string}', async function (num) {
  await new calculate().click_operator(num)
})
Then('I verify the result is {string}', async function (num) {
  await new calculate().verify_result(num)
})

Now let’s create a page(We are using Page Object Model (POM) structure here) file where we have to declare the class and all the methods cleaning in step definitions.

Following is the code snippet for the page file.

const { Before } = require('@cucumber/cucumber')
var { setDefaultTimeout } = require('@cucumber/cucumber')
setDefaultTimeout(60 * 1000)
const {
  openBrowser,
  goto,
  write,
  click,
  $,
  closeBrowser,
  setConfig,
  button,
  waitFor,
} = require('taiko')
const assert = require('assert')
setConfig({ observeTime: 30000, retryTimeout: 30000, navigationTimeout: 30000 })
class calculate {
  async launch() {
    await openBrowser({ headless: false })
    return goto('calculator.net')
  }
  async click_number(num) {
    waitFor(5000)
    await click($('span[onclick="r(' + num + ')"]'))
  }
  async click_operator(num) {
    await click($(`span[onclick="r('` + num + `')"]`))
  }
  async verify_result(num) {
    waitFor(5000)
    assert.equal(await $('#sciOutPut').text(), num)
    closeBrowser()
  }
}
module.exports = calculate

And, In the above code snippet, methods contain actions like opening the browser, visiting a website, CSS selectors and actions to be performed on it (click), and Assertions. You may notice that we are doing open browser and close browser actions into this page file itself which is not the best practice. You can move it to Before/After hooks and create a nice framework. However, that is for a later blog 🙂

So, to execute the test case, you can run the following command from the terminal.

npx cucumber-js --publish

Happy testing !!!

Conclusion:

We can automate tests using taiko with Cucumber with JavaScript very easily. Taiko is a very powerful tool and easy to implement. It will definitely compete with Selenium and Playwright in the coming years.

Read more blogs here

Single Sign On with Multi Factor Authentication using Cypress 10

Single Sign On with Multi Factor Authentication using Cypress 10

Introduction:

With Cypress 10 we can automate  Single Sign On with Multi-Factor Authentication enabled. The new feature of Cypress 10 helps us to visit multi-domain in a single test. Now the cy.origin module helps us to do the same. We can visit the Single Sign-On site and make users authenticate and redirect to our main application domain. But usually, people find it difficult to automate Single Sign On-based applications and have multi-factor authentication for security. This Blog will help us to overcome both the challenges of Single Sign On with Multi-Factor Authentication using Cypress 10 and using a session that will make our test fast, saves time of login, and authenticate once login multiple times.

Test Goal: Automate Office 365 using Cypress 10

To Overcome both the challenges of SSO and the MFA  using Cypress 10 and using session.

What are the test requirements?

  1. Cypress 10 installed.
  2. The secret key for Office 365 account.
  3. And one npm package.

Let’s start the recipe.

How do I get a secret key for an office 365 account to generate OTP:

6th Choose the authenticator app option

7th Click on the link I want to use a different authenticator app 

8th Click on next

9th Click on the button can’t scan the image

10th Copy the secret key and paste it and click on next.

Installing NPM packages required using one of the below methods:-

  • npm i -D cypress-OTP
  • yarn add -D cypress-OTP

Single Sign On with Multi-Factor Authentication using Cypress 10

We are all set for the next steps now next part is in VS code

Open VS code 

Solution:

Go to supports/command.js

  • This is a custom command, we can use this command anywhere in the cypress test. We have to add parameters such as name, Email id, password, and token as a secret which we extracted for office 365 accounts. “login” is the command name we can use using cy.login()in our test.
Cypress.Commands. add("login", (email, password, token, path) => {
  const args = { email, password, token }
  • Visit the application URL
cy.visit(Cypress.config('apiLoginURL')) 
  cy.on('uncaught:exception', (err, runnable) => {
    return false
  })
  cy.wait(7000)
  • Here we have to click on the button which navigates us to the office 365 pages where we have to authenticate the user. This landed us on the office365 SSO page. 
cy.get('.microsoft').click()
  • The new feature for Cypress 10 is cy.origin which helps us navigate to multi-domain in the same test. Here we will pass the login URL for office 365.
 cy.origin('https://login.microsoftonline.com', { args }, ({ email, password, token }) => {
    cy.wait(7000)
    cy.get('body').then(body => {
      if (body.find('#otherTileText').length > 0) {
        cy.contains('Use another account').click()
  • Passing the Email address for office 365
cy.get('#i0116').type(email)
      }
      else {
        cy.get('#i0116').type(email)
      }  
    cy.get('#idSIButton9').click()
    cy.wait(3000)
  • Passing password for office 365
cy.get('#i0118').type(password)
    cy.contains('Sign in').click()
    cy.get('div.tile:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2)').click()
  • Now we have to pass the secret key which we have extracted in the above steps and pass it to the OTP-generate task. This step is responsible for generating the secret.
 cy.task("generateOTP", token).then(token => {
      cy.get("#idTxtBx_SAOTCC_OTC").type(token);
      cy.get('#idSubmit_SAOTCC_Continue').click()
      cy.wait(3000)
    })

For any exception, we are having the below block

 cy.on('uncaught:exception', (err, runnable) => {
      return false
    })
        cy.get('#idBtn_Back').click()
        cy.wait(16000)  
        })              
}) 
})
  • Now we have to use this custom command in our test. Generally, we have to log in each time before the actual test. So we can use Cypress hooks ‘before’ and ‘before each’ hook.

Let’s see the code

Create a file and name it ‘beforeafter.js’ inside the Cypress folder. And paste the below Code

before(() => {

Here, we are creating the session

  cy.session('1', () =>{

This is a custom command we are calling in the command.js file. It passes all required parameters like email, password, and Secret key.

cy.login(Cypress.env("email"),Cypress.env("password"),Cypress.env('secret'),Cypress.env('file'))
  })   
  })
beforeEach(() => {

  Here we are restoring the above session which will help us to authenticate the user and log in quickly to the application.

cy.session('1')
cy.wait(5000)
cy.visit(Cypress.config('apiLoginURL'))
cy.get('.microsoft').click()
cy.wait(15000) 
})

Usage in test

Cypress/Support/Command.js

Cypress.Commands.add("login", (email, password, token, path) => { 
  const args = { email, password, token }
  cy.visit(Cypress.config('apiLoginURL'))
  cy.on('uncaught:exception', (err, runnable) => {
    return false
  })
  cy.wait(7000)
  cy.get('.microsoft').click() 
  cy.origin('https://login.microsoftonline.com', { args }, ({ email, password, token }) => {
    cy.wait(7000)
    cy.get('body').then(body => {
      if (body.find('#otherTileText').length > 0) {
        cy.contains('Use another account').click()
        cy.get('#i0116').type(email)
      }
      else {
        cy.get('#i0116').type(email)
      }
    cy.get('#idSIButton9').click()
    cy.wait(3000)
    cy.get('#i0118').type(password)
    cy.contains('Sign in').click()
    cy.get('div.tile:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2)').click()
    cy.task("generateOTP", token).then(token => {
      cy.get("#idTxtBx_SAOTCC_OTC").type(token);
      cy.get('#idSubmit_SAOTCC_Continue').click()
      cy.wait(3000)
    })
    cy.on('uncaught:exception', (err, runnable) => {
      return false
    })  
        cy.get('#idBtn_Back').click()
        cy.wait(16000)      
        })            
}) 
})

Usage in test

BeforeAfter.js file

before(() => {
  cy.session('1', () =>{
    cy.login(Cypress.env("email"),Cypress.env("password"),Cypress.env('secret'),Cypress.env('file'))
  })
  })
Before(() => {
  cy.session('1')
cy.wait(5000)
cy.visit(Cypress.config('apiLoginURL'))
cy.get('.microsoft').click()
cy.wait(15000)
})

Conclusion:

We are successfully able to automate login to the application through SSO and automate MFA and reduce the login time using a session in Cypress. Here is a link to another good read around the same topic.

Read more blogs here

How to Create Click-up issues on failed tests using Cypress?

How to Create Click-up issues on failed tests using Cypress?

The purpose of this blog is to provide an overview of Create Click-up issues on failed tests using Cypress for reporting bugs during testing or when automated tests are failed. Click-up is a cloud-based collaboration and project management tool suitable for all team sizes to adopt as an Application Lifecycle Management tool. In our context, we will use it to create bug tasks for failed tests.

In this blog, we will have a quick overview of:-
  • What is Click-up?
  • What is Click-up API and how to use them?
  • How to automate the Click API using cypress.
  • How To create a task automatically when a Cypress test fails with Screenshots.
Requirements:-
  • Node.js to be installed.
  • Any IDE recommended is VS Code.
  • Cypress 10 or any version of Cypress installed.
  • Click-up account.
Now let’s start with our agenda

What is Click-up?

It is a project management tool that is easy to customize and use.

What are Click-up API and how do use them?

Click API  is a service provided by Click-up through which you can operate Click-up without UI.

You can find more information about this by visiting this page:-https://clickup.com/api

We can use them through API client tools like the postman or you can use them in any programming language/ technology which supports HTTPS protocols. In this blog, we are using the Cypress testing tool which is Node.js-based technology using JavaScript as a programming language.

How to automate the Click-API using Cypress?

Cypress’s cy.request API module helps you to automate API quickly.

How to create a task automatically when a Cypress test fails with Screenshots.

Requirements.
  • Personal access token for Click-up.
  • Postman or any API client tool.
  • List Id, Folder Id, Space Id, and Team id of Click-up workspace. 
Let’s see how can we get the personal access token for Click-up:-
  1. Navigate to your personal Settings. 

2. Click Apps in the left sidebar.

3. Click Generate to create your API token.

4. Click Copy to copy the key to your clipboard.

Note:- You can use this personal access token in API for authentication. 

 Get the Team Id 

This endpoint is used to view teams: user groups in your workspace. Use the following API request in postman or any other API client tool for getting the team id which needs to pass as a URI parameter to get the Space id.

Request:- https://api.clickup.com/api/v2/team

Method:- GET

Headers: key:- Authorization

Value:-Personal access token 

Response JSON:

{
    "teams": [
        {
            "id": "33131480",
        }
             ]
 }
Now we need to get the Space Id

View the spaces available in a workspace.

Request:-https://api.clickup.com/api/v2/team/<team_id>/space

Method:-GET

Headers: key:- Authorization

               Value:-Personal access token 

Response JSON

{
    "spaces": [
        {
            "id": "1236491",
        }
              ]
}

Till now we have fetched responses of two endpoints

Let’s get the folder id 

View the folders in space.

Request:-https://api.clickup.com/api/v2/space/<space_id>/folder

Method:-GET

Headers: key:- Authorization

               Value:-Personal access token 

Response JSON

{
    "folders": [
        {
            "id": "34231390",
            "name": "Product Roadmap",
            "orderindex": 0,
            "override_statuses": true,
            "hidden": false,
            "space": {
                "id": "3376891",
                "name": "New Space"
                     }
       }
               ]
}
Now The last thing to get is a List of id 

View the lists within a folder.

Request:- https://api.clickup.com/api/v2/folder/<folder>/list

Method:-GET

Headers: key:- Authorization

               Value:-Personal access token 

Response JSON:

{
    "lists": [
        {
            "id": "7623172",
            "name": "Jan",
            "orderindex": 2,
            "status": null,
            "priority": null,
            "assignee": null,
            "task_count": 2,
            "due_date": "1612045800000",
            "start_date": "1609453800000",
            "folder": {
                "id": "3488090",
                "name": "Product Roadmap",
                "hidden": false,
                "access": true
        }
      }
     ]
}

We have fetched all the required Uri parameters for getting the list id now we can use this list id as a Uri parameter for generating tickets through automation using cypress

  •  How to create a task automatically when a cypress test fails with screenshots.

We have successfully extracted the list id which we will be using as a Uri parameter for creating a task. The cypress “command.js” file is a  file where we have to place reusable code inside that we have the “cypress.command.add” method so we can use that code anywhere in cypress tests.

Paste the following code in Cypress/ Supports/ Command.js

Below is create task method name and we are passing the feature name, scenario name, and steps as a parameter which will be added to the ticket in Click-up

Cypress.Commands.add("CreateTask",(featurename,scenarioname,allsteps)=>

Now to call the endpoint with cy.request we will require the endpoint, the request method, and the access token, so we will store everything in a variable and access in the below code.  mention all endpoints, request methods, and access tokens as variables. 

let endpoint = 'https://api.clickup.com/api/v2/list/<listid>/task'
    let req = 'POST'
    let token = Access_Token '
    const headers = {
        'Authorization': token,
        'Content-Type': 'application/json'
                    }

We are passing all the variables to the cy.request which we mentioned above.

Calling API:- This API will create a ticket for us and the response that we yield will give the id of the ticket which we can use to attach a screenshot in the next request.

cy.request({ method: req, url: endpoint, 'headers': headers, body: body }).then((response) => {
        cy.log(JSON.stringify(response))
        id = response.body.id
        Getting the Response 
        cy.log(id)

In this case, we are getting the id of the ticket which we can use in the next request as a Uri parameter. Now we need to get the path of the failed screenshot dynamically and pass it to the API so that screenshot should get attached to the same click-up ticket.

The below code will attach a screenshot to the ticket which we have created through automation.

const screenshotsFolder = Cypress.config("screenshotsFolder");
         
            const screenshotFileName = `${featurename} -- ${scenarioname} (failed).png`;
Attaching ScreenShot
            cy.request("https://clickup.com")
 var data = new FormData();
            data.append("filename", screenshotFileName);
            cy.log(id)

We have to attach a screenshot as multipart form data and pass it as a Blob as a request for the above ticket which we have created. Here we have used XMLHttp request as cy.request does not support multipart form data.

                cy.intercept({
                method: "POST",
                url: "https://api.clickup.com/api/v2/task/"+id+"/attachment?custom_task_ids=false&team_id=3344860"
            }).as('uploadFile')
                .window()
                .then((win) => {
                    cy.wait(3000)

Here we are reading the screenshot file and creating Blob and passing it as a binary.

cy.readFile(`${screenshotsFolder}/${Cypress.spec.name}/${screenshotFileName}`, 'binary')
                        .then((binary) => Cypress.Blob.binaryStringToBlob(binary))
                        .then((blob) => {
                            const xhr = new win.XMLHttpRequest();
                            data.set("attachment", blob, `${screenshotsFolder}/${Cypress.spec.name}/${screenshotFileName}`);
                            xhr.open("POST", "https://api.clickup.com/api/v2/task/"+id+"/attachment?custom_task_ids=false&team_id=3344860");
                            let token = Access_Token

We have used XMLHttp request to process and send multipart form data in the request.

 xhr.setRequestHeader('Access-Control-Allow-Headers', '*')
                            xhr.setRequestHeader("Authorization", token);
                            xhr.send(data);
                        })
                })
            cy.wait('@uploadFile').then((response) => {
                cy.log(response)
            })
})
}) 

Now when the test fails this part of the code will get executed which will call the method mentioned in the command.js file and automatically create the ticket with a failed screenshot and description.

Paste the code in Cypress/Beforeafter.js.
afterEach(() => {

This Code Will Execute after each test
   cy.on("test:after:run", (test, runnable) => {
      setTimeout(() => {
            Cypress.env("ok",test)
      }, 4000);         
        }).then(()=>
        {

Now we have to extract the feature file name, scenario name, and steps so that we can pass it to the Click-up ticket. We have a test state as a window object which yields all the properties related to the test like test name, test status, etc…

cy.wait(6000)
          let chip = Cypress.env("ok")
          let status = chip.state
          let scenario = chip.title
          let allsteps = ""
          let Featurename = testState.gherkinDocument.feature.name
       let current = chip._testConfig.testConfigList[0].overrides.env.__cypress_cucumber_preprocessor_dont_use_this.currentStep.pickleStep.text
         let a = chip._testConfig.testConfigList[0].overrides.env.__cypress_cucumber_preprocessor_dont_use_this.allSteps
          for (let index = 0; index < a.length; index++) {
              const element = a[index]      
              allsteps += element.pickleStep.text
          }

Calling the custom command which will create a ticket every time the test fails. Passing the feature name scenario name and all steps as parameters to be added to the ticket.

This will create a ticket in Click-up. 

cy.CreateTask(Featurename,scenario,allsteps)
        })
                })
  • The task will be created in Click-up as shown below in the screenshot:-
Conclusion:-

We can create tasks with screenshots attachments for failed test cases using click-up APIs and automate them using Cypress automation.

Read more blogs here

How to automate office 365 login in cypress using Nightwatch.js

How to automate office 365 login in cypress using Nightwatch.js

This blog will explore how to automate office 365 login in cypress using Nightwatch.js. Currently, I’m working on Cypress automation for testing web applications.

First, let’s understand the problem area.

We needed to automate the login flow in an application using office 365’s IDP. When we launched the application through cypress, we landed on the office 365 login screen which was not possible to automate. We were not able to have control over the page through cypress. Cypress has some other techniques to automate this like SSO(single-sign-on) However, our clients were not able to share some secrets and also the Azure DevOps environment.

So far nothing worked for us, So now let’s come to the point.

In order to automate office 365 login in cypress using Nightwatch.js. I thought we could use other tools to get the JWT token that we can use to log into an application through Cypress.

Now, the question was which tools to be selected

I selected Nightwach.js tools because it was easy to use with little configuration. We successfully automated office 365 login. Get the JWT token from local storage and write it in common utility files.

Again the question was how to trigger the Nightwatch.js script through cypress.

Cypress supports cy. exec command runs the Nightwatch.js script and then control comes back to cypress and the rest execution is done.

  • Now let us try the recipe, First install nightwatch.js with the following command, Copy
npm install nightwatch						 
  • And also need to install the chrome driver with the following command, Copy
npm install chromedriver --detect_chromedriver_version

And also created a script for nightwatch.js

In the below code, we are reading JWT Token from local storage and storing it into jwtToken.txt

module.exports =
{
  '@tags': ['adm'],
  "test": function (browser) {
    let ch
    browser.url("Application url")
    browser.click('#details-button')
    browser.click('#proceed-link')
    browser.click('span[class="microsoft-logo"]')
    browser.setValue('#i0116', 'email')
    browser.click('#idSIButton9')
    browser.setValue('#i0118', 'password')
    browser.click('#idSIButton9')
    browser.click('#idBtn_Back')
    browser.execute(function () {
      ch = window.localStorage.getItem('jwtToken');
      return ch
    }, [], function (result) {
      this.assert.equal(result.value, result.value
      const fs = require('fs'
      const path = require('path');
      const dirPath = path.join(__dirname, '../cypress/fixtures/jwtToken.txt')
      var path2 = dirPath.replace(/\\/g, "/");
      console.log(path2)
      fs.writeFile(path2, result.value, err => {
        if (err) {
          console.error(err)
          return
        }
      })
    })
  }
}				
  • Add the following command in support/command.js
Cypress.Commands.add("User", () => {
  cy.exec("npx nightwatch -e chrome ")
})
  • Add the following code in the hooks file to use the JWT token in your tests reading the jwtToken.txt
Before({ tags: "@user" }, () => {
  cy.readFile('cypress/fixtures/jwtToken.txt').then((jwtToken) => {
    cy.log("JWT Token before : " + jwtToken)
  })
  cy.log('start')
  cy.User()
  cy.log('end')
  cy.readFile('cypress/fixtures/jwtToken.txt').then((jwtToken) => {
    cy.log("JWT Token After : " + jwtToken)
    cy.window().then(
      window => window.localStorage.setItem('jwtToken',jwtToken), { flag: 'w' })
      cy.visit(Cypress.config('apiLoginURL'))
  })
})

The output is that a new JWT token is generated and that the JWT token is used to run cypress test cases.

JWT Token in Cypress Test

Conclusion :

 Different automation tools can be integrated with Cypress where Cypress lacks the ability to do so. In the previous version of Cypress, multi-domain origins were not supported, so we automated this scenario through Nightwatch.js.

Read more blogs here