Tips for creating a blank locator in Playwright with Typescript

Currently facing a challenge in creating a custom selector for my Playwright tests. I'm struggling to generate an empty locator. The purpose of this is to input a string of test IDs and retrieve a nested element, thereby enhancing readability in my tests. The desired outcome would be something along these lines:

export class CustomPage {
   protected readonly page: Page;
   
   constructor(page: Page) {
      this.page = page;
   }

   public getNestedElement(testIds: Array<string>) {
      let locator = //initialize locator with this.page

      foreach(const id of testIds) {
         locator = locator.getByTestId(id);
      }

      return locator;
   }
}

Your assistance on this matter would be greatly appreciated!

Answer №1

It is indeed possible to achieve what you are asking for:

public getNestedElementByTestIds(testIds: Array<string>) {
  if (testIds.length === 0) {
    throw Error("testIds must contain at least one element");
  }

  let locator = this.page.getByTestId(testIds[0]);

  for (const testId of testIds.slice(1)) {
    locator = locator.getByTestId(testId);
  }

  return locator;
}

Here is a runnable demonstration:

const playwright = require("playwright"); // ^1.30.1

const html = `
<div data-testid="foo">
  ignore this
  <div data-testid="bar">
    ignore this
    <div data-testid="baz">
      ignore this
      <div data-testid="quux">
        PRINT ME
      </div>
    </div>
  </div>
</div>`;

const getNestedElementByTestIds = (page, testIds) => {
  if (testIds.length === 0) {
    throw Error("testIds must contain at least one element");
  }

  let locator = page.getByTestId(testIds[0]);

  for (const testId of testIds.slice(1)) {
    locator = locator.getByTestId(testId);
  }

  return locator;
}

let browser;
(async () => {
  browser = await playwright.chromium.launch();
  const page = await browser.newPage();
  await page.setContent(html);
  console.log((await getNestedElementByTestIds(page, [
    "foo", "bar", "baz", "quux"
  ]).textContent()).trim()); // => PRINT ME
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

However, keep in mind that this approach may be overly complex. It is recommended to limit the chaining of getByTestIds in the calling code, or employ a more reliable selection method rather than relying solely on test ids and emphasizing direct, user-visible properties.

In essence, it's best not to abstract Playwright calls into helpers unnecessarily, even if it means duplicating some code across tests. Playwright commands are already quite high-level, and anything beyond a simple POM setup could pose challenges in terms of maintenance for anyone other than the original author.

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

Cannot assign type void to 'Intrinsic Attributes & Dispatch<SetStateAction<>>'

Just starting out with typescript and ran into this error: Error :Type '{ setTasks: void; }' is not assignable to type 'IntrinsicAttributes & Dispatch<SetStateAction<IStudy[]>>'. Property 'setTasks' does not e ...

Validation of passwords containing special characters in Angular2

I am working on setting up password validation requirements to ensure the field contains: Both uppercase letters, lowercase letters, numbers and special characters This is my current progress: passwordValueValidator(control) { if (control.value != u ...

Tips for arranging datasets in React Chart.js 2 to create stacked bar charts

Currently, I am facing an issue with sorting the datasets displayed in each stacked bar chart in descending order as they are showing up randomly. Here is the snippet of my code: import React from 'react'; import { Chart as ChartJS, CategoryS ...

React is unable to assign a class field beyond the scope of axios

class App extends React.Component { app: Application; ... componentDidMound() { axios.get(…).then(res => { this.app.currentUser = res.data.data; // setting value inside lambda function. console.log(this.app.currentUser); // ...

Can a form be submitted using ViewChild in Angular5?

Can a form be submitted using ViewChild in Angular5? If so, how can it be achieved? I attempted to do this but was unsuccessful My Attempt: <form #form="ngForm" (submit)="submitForm(form)" novalidate> <input required type="text" #codeReques ...

Acquire request data prior to exiting function in React

I am working on a NextJS application that utilizes axios for making requests to a backend API, which requires an authentication token. To handle this, I have implemented a function that retrieves the auth token and stores it in a variable at the module-lev ...

Compile time extraction of constant string from type field

I am currently facing an issue with a field in my type that contains a constant string literal. My goal is to be able to reference both the type and field by name so that I can utilize this string literal throughout my code. Here is an example: export type ...

Classes that are defined as static or instance pointers

Background: In my current project, I have a set of three classes related to a game. In the past, I developed games using Unity where components like the camera were accessed through functions available throughout all code. However, in this project, each c ...

NativeScript Error Code NG8001: Element 'ActionBar' is unrecognized

In my project, the startupscreen module setup is as follows: import { NativeScriptFormsModule } from "@nativescript/angular"; import { NativeScriptCommonModule } from "@nativescript/angular/common"; import { NgModule, NO_ERRORS_SCHEMA } ...

When working with Typescript and React/JSX, ensure that the type of JSX element attributes 'T' is defined as an object type

Currently using TypeScript version 1.8.10 in combination with Visual Studio 2015, I encountered the following error when trying to implement react-router: import * as React from "react"; import * as ReactDOM from "react-dom"; import { Router, browserHisto ...

How does the keyof operator fetch non-enumerable inherited properties from an object literal type?

Take a look at this TypeScript code: 'use strict'; type Value = 1 | 2 ; type Owner = 'ownerA' | 'ownerB'; type ItemType = 'itemTypeA' | 'itemTypeB'; type Item = { type: ItemType; owner: Owner; value: ...

Retrieving the coefficients of a polynomial using istringstream in C++

Currently, my focus is on a project where I am developing a polynomial class. I have successfully implemented methods for addition, subtraction, division, and more. However, I am facing an issue with a method that converts a string like "3x^1-2x^4" into a ...

Obtain the Enum's Name in TypeScript as a String

I am currently looking for a solution to transform the name of an enum into a string format. Suppose I have the following Response enum, how can I obtain or convert 'Response' into a string? One of my functions accepts any enum as input and requi ...

The npm start command is unable to recognize the tsx file

I recently attempted to create a React app and incorporate TypeScript into it. However, upon running the app, I noticed that npm start was not recognizing the TypeScript file and failing to automatically generate tsconfig.json. Here is the sequence of ste ...

Attempting to form an array from the elements within an array of child objects

I need assistance with extracting child objects from an array in Angular7. The array currently has a top level that I want to remove, leaving only the objects with properties. Any guidance on how to achieve this would be greatly appreciated. { "toplev ...

Excluding an entity field from being returned in a JSON response can be achieved by utilizing NestJS and TypeORM

I need to omit the password field from my returned JSON data. I have been working with NestJS and Typeorm for this project. Although I have tried implementing the solution mentioned in this Stack Overflow post, it did not work as expected in my NestJS se ...

In the past, it was impossible to access all properties simultaneously from a TypeScript union

Recently, I purchased an online course that I had put off watching until now. In the course, it specifically mentioned that certain code in TypeScript was not allowed: type Name = { name: string } type Age = { age: number } type UnionBoth = Name | Age co ...

Steps for showcasing the value received from the server on the browser console

I'm having some trouble retrieving a value from a server using the httpclient get method in Angular. Despite my efforts, I can't seem to see it displayed on either the console or the webpage. Can anyone help me figure out what's going wrong? ...

The Typescript hello world example encounters an issue with Karma

Recently, I encountered an issue while working on a TypeScript project with Jasmine and Karma. It seems that Karma is unable to execute the compiled unit tests due to an error showing up in Chrome: Uncaught ReferenceError: define is not defined To illust ...

Is casting performed by <ClassName> in typescript?

According to the Angular DI documentation, there is an example provided: let mockService = <HeroService> {getHeroes: () => expectedHeroes } So, my question is - can we consider mockService as an instance of HeroService at this point? To provide ...