How can I utilize identical cucumber steps for both mobile and web tests while evaluating the same functionality?

In order to test our website and React Native mobile app, we have developed a hybrid framework using webdriver.io and cucumber.io. We currently maintain separate feature files for the same functionality on both the web and mobile platforms.

For example, in Mobile_getMembersDetails.feature:

@mobile
Scenario: As a user, I can view the list of members on the home screen
    When I navigate to the home screen
    Then I should see the list of members displayed

The corresponding step implementation is as follows:

@when(/^I am on the home screen$/)
public async whenIamOnHomeScreen() {
await mobileLandingPage.clickContinueButton();
await mobileHomePage.clickAllowButton();
expect(await mobileHomePage.isHeaderDisplayed()).toBe(true);
}

@then(/^I should see the members list on mobile home screen$/)
public async thenIshouldSeeMemberslistOnMobileHomeScreen() {
expect(await mobileHomePage.isMemberListIsDisplayed());
}

Furthermore, in Web_getMembersDetails.feature:

@web
Scenario: As a user, I can see the list of members on the home page
    When I visit the home page
    Then I should see the list of members displayed on the web page

The corresponding step implementation is as follows:

@when(/^I am on the home page$/)
public async whenIamOnHomePage() {
webHomePage.open();
expect(await webHomePage.isPageLoaded()).toBe(true);
}

@then(/^I can see the members list on web home page$/)
public async thenIshouldSeeMemberslistOnWebHomePage() {
expect(await webHomePage.isMemberListIsDisplayed()).toBe(true);
}

Although the functionality remains the same, the navigation and implementation differ between web and mobile. We are exploring ways to consolidate this into a single feature file and set of steps. One approach is to introduce @web and @mobile tags and determine the test type based on the execution command. However, this introduces complexity with multiple conditional statements. Is consolidating into one feature file not recommended? Your insights would be appreciated. Thank you.

@web @mobile
Scenario: As a user, I can view the list of members on the home page
    When I visit the home page
    Then I should see the list of members displayed on the web page

The proposed step implementation is as follows:

@when(/^I am on the home page$/)
public async whenIamOnHomePage() {
  if(driver.isMobile()){
     await mobileLandingPage.clickContinueButton();
     await mobileHomePage.clickAllowButton();
     expect(await mobileHomePage.isHeaderDisplayed()).toBe(true);
    } else {
     webHomePage.open();
     expect(await webHomePage.isPageLoaded()).toBe(true);
     }
  }

@then(/^I can see the members list on web home page$/)
public async thenIshouldSeeMemberslistOnWebHomePage() {
if(driver.isMobile()){
    expect(await mobileHomePage.isMemberListIsDisplayed());
   } else {
    expect(await webHomePage.isMemberListIsDisplayed()).toBe(true);
     }
  }

Answer №1

Instead of creating separate mobileHomePage and webHomePage classes, consolidate them into a single homePage class. When initializing the homePage, determine the type of web driver being used.

if(driver.isMobile())
  homePage = new MobileHomePage(driver);
else
  homePage = new WebHomePage(driver);

This approach simplifies the process as you only need to verify once for each page:

@when(/^I am on the home page$/)
public async whenIamOnHomePage() {
  homePage.openHomePage();
  expect(await homePage.isPageDisplayed()).toBe(true);
}

@then(/^I can see the members list on web home page$/)
public async thenIshouldSeeMemberslistOnWebHomePage() {
    expect(await homePage.isMemberListIsDisplayed()).toBe(true);
}

To enable this, ensure that both the MobileHomePage and WebHomePage classes implement the same HomePage interface. For example, you will need to create an openHomePage method that navigates from the mobile landing page to the home page, or directly opens the web home page. Similarly, the isPageDisplayed method should be implemented accordingly.

Answer №2

One intriguing aspect of this issue is exploring the intersection between web and mobile on a feature/scenario level.

If your features and scenarios are abstract enough that the behavior is consistent across both native and website platforms, you can explore options where the same step triggers different implementations. This process involves two key decisions:

  1. Determining how to instruct cucumber to test either native or website
  2. Identifying where in your implementation to branch out based on this information

You may then execute cucumber tests separately for native and website environments as needed.

If there are distinct behaviors specific to either platform, you can choose to:

  1. Create separate features and implementations for each platform
  2. Opt for a single set of features with clear conventions (e.g. using @native and @website tags) to differentiate between native and website functionalities within steps

Alternatively, a combination approach might involve:

features
native_features
website_features

With diligent refactoring, this combined solution can work seamlessly. For instance:

  1. Develop new native functionalities in native_features
  2. Explore equivalent website functionalities
  3. Merge native and website functionalities into a unified approach through refactoring

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Open new tab for Angular OAuth2 OIDC login process

Currently, I am incorporating the authorization code flow using angular-oauth2-oidc in my Angular application. It is a fairly straightforward process. However, I would like to have the ability for the login flow to open in a new tab when the login button ...

Inject a DOM event into a personalized form validator within an Angular application

I'm currently working on validating a form using the reactive approach. I've implemented a file input to allow users to upload files, with custom validation conditions in place. However, I'm encountering an issue where the validator only rec ...

Delay in Updating Nativescript Slider Value

After developing a metronome using the Nativescript Slider here to adjust speed (interval), I encountered an issue with incorrect updating of values. The code initially worked well, allowing real-time speed changes: app.component.html <Slider #sl min ...

Mastering the art of utilizing GSI Index and FilterExpression in AWS DynamoDB querying

In my DynamoDB database table, I have the following structure. It consists of a primary key (ID) and a sort key (receivedTime). ID(primary key) receivedTime(sort key) Data ID1 1670739188 3 ID2 167072 ...

Using Typescript to identify the specific subtype of an object within a union type based on the properties it contains

I am trying to create a carousel that displays items of two different types (FirstType, SecondType). The carousel component I am using requires an array as input, so I have declared the items as an array union like this: type FirstType = { a: 'first ...

Encountering an error while trying to update an Angular application to version 10

I am currently running a demo app and I am new to Angular. Below is my category list component. import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Observable } from 'rxjs'; im ...

Refresh a page automatically upon pressing the back button in Angular

I am currently working on an Angular 8 application with over 100 pages (components) that is specifically designed for the Chrome browser. However, I have encountered an issue where the CSS randomly gets distorted when I click the browser's back button ...

Can a TypeScript function be structured to return never (or throw) if a generic type extends a subtype without requiring casting?

(This code snippet is purely for demonstration purposes, as no real use-case exists here) I am attempting to create a function that throws an error if the input string is equal to "fish". I have achieved this using the as keyword, but I am curious if ther ...

Tips for efficiently displaying and handling vast quantities of information within an Angular 2 table

I am currently working on the development of an Angular 2 application that requires displaying a large amount of data in a scrollable table. The data is stored in an array within my component class: export class AppComponent { clients: any[] = []; ...

What is the reason behind Angular's renderer.listen() causing the text to be deselected?

(Background: my project involves developing an email processor that displays emails on a web page) To enable click events in a specific area, I leverage Angular's Renderer2. This approach is necessary because the content is dynamically generated with ...

What is the purpose of the 'unique' keyword in TypeScript?

While coding in the TypeScript playground, I stumbled upon a situation where the term unique seems to be reserved. However, I haven't been able to find any official documentation regarding this. https://i.stack.imgur.com/eQq5b.png Does anyone know i ...

Why is NestJs having trouble resolving dependencies?

Recently delving into NestJs, I followed the configuration instructions outlined in https://docs.nestjs.com/techniques/database, but I am struggling to identify the issue within my code. Error: Nest cannot resolve dependencies of the AdminRepository ...

Can you explain the meaning of '<Hero[]>' in programming jargon?

Hello there! I am new to learning angular and typescript, and currently going through a tutorial at angular. However, I stumbled upon something that I find confusing. For example: 1. getHeroes(): Observable<Hero[]> { this.messageService.add(&ap ...

What is the best way to create and manage multiple collapsible Material-UI nested lists populated from an array with individual state in React

In my SideMenu, I want each list item to be able to expand and collapse independently to show nested items. However, I am facing the issue of all list items expanding and collapsing at the same time. Here is what I've attempted: const authNavigation ...

What is the best way to utilize a single component for validating two other components?

I am encountering an issue with my components setup. I have three components in total: GalleryAddComponent, which is used to add a new element, GalleryItemComponent, used to edit an element, and FieldsComponent, the form component utilized by both GalleryA ...

Subtracting Arrays Containing Duplicates

Imagine having two arrays defined like this: const A = ['Mo', 'Tu', 'We', 'Thu', 'Fr'] const B = ['Mo', 'Mo', 'Mo', 'Tu', 'Thu', 'Fr', 'Sa&ap ...

What is the method for including a dynamic image within the 'startAdornment' of MUI's Autocomplete component?

I'm currently utilizing MUI's autocomplete component to showcase some of my objects as recommendations. Everything is functioning correctly, however, I am attempting to include an avatar as a start adornment within the textfield (inside renderInp ...

I am unable to locate the appropriate TypeScript type for the `displayName` attribute when it is used as a prop for the `type` component

Having trouble finding the correct type as mentioned in the title. After inspecting with DevTools, I have confirmed that every component I am programmatically looking at has Component.type.displayName. For anything that is not an ElementType, the type is a ...

Having trouble with installing Typescript on a MacBook?

I have been attempting to follow the instructions provided on TypeScriptLang.org but for some reason, I am unable to successfully download typescript. This is what I have tried so far: mkotsollariss-MacBook-Pro:/ mkotsollaris$ which node /usr/local/bin/n ...

Experiencing TypeScript error in VSCode while trying to run in Nodejs? Here's how to troubleshoot and resolve

Experimenting with the performance measurement code provided by Nodejs in VSCode has been an interesting challenge for me. I encountered no issues running the code in Nodejs, and it executed smoothly. However, when attempting to run the code in VSCode, er ...