Inheritance from WebElement in WebdriverIO: A Beginner's Guide

I am seeking a solution to extend the functionality of the WebElement object returned by webdriverio, without resorting to monkey-patching and with TypeScript type support for autocompletion. Is it possible to achieve this in any way?

class CustomCheckbox extends WebdriverIOWebElement {
    constructor() {
       super($('div'))
    }
    
    // Overriding base method
    isDisplayed(): boolean {
        // Add new logic here
    }

    check() {
        if(!this.isChecked()) {
            this.click()
        }
    }

    uncheck() {
        if(this.isChecked()) {
            this.click()
        }
    }
}

Answer №1

Imagine a scenario where we have a new tag (my-app) in HTML and need to create a login case using the webdriverIO.

This is the HTML structure:

To avoid repetitive code, we can utilize the component object pattern. This pattern aims to minimize repetition and centralize the component's API into its object. To interact with an element's shadow DOM, we first require the host element. Utilizing a base class for component objects simplifies this process.

class Component {

  constructor(host) {
    const selectors = [];
    // Traverse back to the browser object and store all selectors
    while (host.elementId && host.parent) {
      selectors.push(host.selector);
      host = host.parent;
    }
    selectors.reverse();
    this.selectors_ = selectors;
  }

  get host() {
    // Starting from the browser object, reselect each element
    return this.selectors_.reduce((element, selector) => element.$(selector), browser);
  }
}

module.exports = Component;

Subsequently, we create a subclass for our app-login component:

const Component = require('./component');

class Login extends Component {

  get usernameInput() {
    return this.host.shadow$('input #username');
  }

  get passwordInput() {
    return this.host.shadow$('input[type=password]');
  }

  get submitButton() {
    return this.login.shadow$('button[type=submit]');
  }

  login(username, password) {
    this.usernameInput.setValue(username);
    this.passwordInput.setValue(password);
    this.submitButton.click();
  }
}

module.exports = Login;

Now, we implement the component object within our login page object:

const Login = require('./components/login');

class LoginPage {

  open() {
    browser.url('/login');
  }

  get app() {
    return browser.$('my-app');
  }

  get loginComponent() {
    // Create a new instance of our login component object
    return new Login(this.app.$('app-login'));
  }

}

Using this approach enables us to easily integrate the component object into tests for any part of our app utilizing the app-login web component. If changes are made to the internal structure of the web component, only the component object needs updating.

A similar methodology is applied to the Check Box Component by incorporating Shadow DOM support:

public class CheckBox extends Component {
  public CheckBox(element) {
    this.element = element;
  }
  get checkBoxSelector() {
    return this.host.shadow$(element);
  }
  get void toggle() {
    checkBoxSelector().click();
  }
  get void check() {
    if (!isChecked()) {
      toggle();
    }
  }
  get void uncheck() {
    if (isChecked()) {
      toggle();
    }
  }
  get boolean isChecked() {
    return checkBoxSelector().isSelected();
  }
}

We then introduce a Check Box Controller component to manage the checkbox instances:

const CheckBox= require('./components/CheckBox');
class CheckBoxController{
  open() {
    browser.url('/login');
  }
  get checkboxComponent() {

    // Verify whether a specific checkbox has been selected or not
    let element = browser.$('[id="lpagecheckbox"]');
    return new CheckBox(element);
  }
}

Note: This serves as a template to guide us towards solving the problem effectively.

Sources:

Answer №2

IWebElement serves as a versatile interface that can be seamlessly incorporated into your driver class.

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

What is the reason for needing to specify event.target as an HTMLInputElement?

I am currently working on a codebase that uses Material Ui with theme overrides. As I set up my SettingContext and SettingsProvider, I find myself facing some syntax that is still unclear to me. Let's take a look at the following code snippet: const ...

Is there a way to personalize the chart tooltip in jqplot to fit my preferences?

I have a chart that displays data. I would like the x-axis labels to show in the format MM/DD, and in the tooltip, I want to display data in the format HH:y-axis. For example, in the chart below, I want the x-axis labels to be 'Oct 15','Oct ...

What is the best way to transfer a user-generated PNG file to my backend server?

I'm in the process of developing a website that enables users to generate personalized graphics and easily download them directly from the platform. My approach involves allowing users to customize an svg using a javascript-powered form, which is the ...

Deciding on excluding empty key:value pairs from an object for various filtering needs

One of the features in my app allows users to filter results by "blood group" and "city", along with other areas. The information is retrieved from a database using Axios for Vuejs, incorporating query strings within the URL. For example: http://example.co ...

Using Three.JS 'OrbitControls' in VueJS application results in a black screen appearing?

Currently, I've been delving into the basic cube exercise on Three.js, and incorporating the three.js cube into my Vue.JS application. Initially, everything was functioning smoothly, with my cube rotating as intended using animate, etc. However, thi ...

How to separate an array of objects into individual arrays using Typescript reduce based on a specific property

I have the following array: statisticsOfScrapDeliveriesItems:[ { supplierId: "0001055404", deliveredFrom: "METALLCO AS", centerId: "C45", materialId: "TS0180", }, { sup ...

Encountering an issue with Next.js, Typescript, and mongoose when attempting to use `let cached = global.mongoose

I attempted to create a cached mongoose connection for my Next.js + Typescript application, but the code I used was: let cached = global.mongoose; if (!cached) { cached = global.mongoose = { conn: null, promise: null }; } The use of global.mongoose res ...

Changing the sliding underline effect in a jQuery navigation bar

Recently, I implemented a sliding underline element in my navigation bar. The idea is that when a link is hovered over, the underline smoothly transitions to that element. You can take a look at the codepen example here: https://codepen.io/lucasengnz/pen/e ...

Switching from a right arrow to a down arrow using jQuery for a collapsible accordion feature

I have developed a unique type of toggle feature similar to an accordion design. When I click on the right-arrow next to an item, such as Area A, it expands to reveal the list of items within Area A. The arrow also changes orientation to point downwards (L ...

Modify the specified pattern with regex only if it does not appear in a particular location in the content

Our tool development heavily relies on regex for various functionalities. One key aspect involves replacing placeholders with actual values dynamically using standard JavaScript's `RegExp`. All placeholder formats are similar to this: {{key}} In mo ...

Is it possible for a Node.js server to specifically generate dynamic HTML, with Nginx handling the distribution of static data, and then automatically deliver the content to the client?

After primarily working with Apache and PHP, I've recently started exploring Nginx and Node.js and have been really enjoying the experience. Initially, I set up an Express server to handle website files and HTML rendering using Handlebars. However, I ...

Click on a div in AngularJS to be directed to a specific URL

I'm currently working on developing an Angular mobile app and I want to be able to navigate to a specific URL, like www.google.com, when a particular div is clicked. Unfortunately, I'm still new to the world of Angular and struggling to achieve t ...

Are there problems with the response values of functions that can handle varying object interfaces?

Currently in the process of developing a command line blackjack app with Node and Typescript, even though I am relatively new to Typescript. My main challenge lies in implementing interfaces for player and dealer objects, as well as creating functions that ...

Tips for streamlining distinct states and generalized state in UI Router

I've set up the following configuration in my JS app under the $stateProvider section using angular ui-router: .state('login', { url: '/login', templateUrl: 'app/login/login.tmpl.html', controller: &apo ...

Having difficulty generating a footer for a page that includes a Material-UI Drawer component

Looking to create a standard full-width footer at the bottom of my page, I need help with the implementation. Utilizing the "Permanent drawer" from Material-UI Drawer for reference here. If you're interested in experimenting with it, CodeSandbox link ...

Is there a way to get Axios to display CSS on the page?

While working on a Web Viewer project with axios for web scraping practice, I encountered an issue where the CSS wasn't loading properly. Here is the code snippet I used: console.log("Tribble-Webviewer is starting!") const express = requir ...

Utilize ng-bootstrap in an Angular CLI project by integrating it with npm commands

I've been attempting to set up a project using Angular CLI with ng-bootstrap, but I'm having trouble getting the style to work properly. Here are the exact steps I followed (as outlined in the get-started page): Create a new project using `ng n ...

Struggling to understand the process of retrieving information from an Axios promise

For my current project, I've been experimenting with using Axios to retrieve JSON data from a json-server to simulate a database environment. While I can successfully display the retrieved data within the .then() block of the Axios function, I'm ...

Incorporate a click function onto the image within the canvas

After creating a canvas and placing an image on it, I realized that I need to include a click event for that image. Below is the code snippet I have written in attempt to achieve this. <canvas id="myCanvas" width="578" height="1000"></canvas> ...

Increasing the ID of a select input using Jquery

Is there a way to modify the identification of my select field? This select option is dynamically created using PHP. The select input can be accessed through var indirectSelect Despite its simplicity, I am facing difficulty in changing it. I attempted th ...