POMAIIIUK
POMAIIIUK

Reputation: 509

How to get element nested item

friends! Few days ago I started my automation tests studying with Cypress. I have problems with getting element.

As for example I provide this HTML layout:

First block

<div class="class_0">
  <div class="class_1"></div>
  <div class="class_2">
    <div class="label_0">
      <div class="label_1">Some text 1</div>
    </div>
  </div>
  <div class="class_3">
    <div class="tooltip_0"></div>
    <div class="tooltip_1">
      <form>
        <button>Button</button>
      </form>
    </div>
    <div class="tooltip_2"></div>
  </div>
</div>

Second block

<div class="class_0">
  <div class="class_1"></div>
  <div class="class_2">
    <div class="label_0">
      <div class="label_1">Some text 2</div>
    </div>
  </div>
  <div class="class_3">
    <div class="tooltip_0"></div>
    <div class="tooltip_1">
      <form>
        <button>Button</button>
      </form>
    </div>
    <div class="tooltip_2"></div>
  </div>
</div>

I want to click on button, but it's possible to find only <div class="label_1">Some text 1</div>. I using this code:

 cy.get('.label_1').contains('Some text 1').parents().find('button').click()

or this:

cy.get('.label_1').contains('Some text 1').parent(). ... .parent().find('button').click()

enter image description here

But I have my own solution, but it's problem to get count of this: <div class="class_0"> block.

Upvotes: 0

Views: 1130

Answers (1)

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48610

You can iteratively search for a parent with a button child. If a button is eventually found, click it.

Here is a recursive function to locate the parent with a button child.

function findCousin(node, childSelector) {
  while (node != null & node.length > 0 && node.find(childSelector).length < 1) {
    node = node.parent();
  }
  return node ? node.find(childSelector) : null;
}

Edit: So, it looks like Cypress uses jQuery behind the scenes. I created a simple shim to emulate your existing functionality wihout altering it.

// *** IGNORE: BEGIN SHIM ***
$.fn.findByContentText = function(text) {
  return this.contents().filter((i, c) => $(c).text().trim() == text.trim());
};
class CyElement {
  constructor(selector) { this.ref = $(selector); }
  contains(text) { return this.ref.findByContentText(text); }
  parent() { return new CyElement(this.ref.parent()); }
  find(selector) { return new CyElement($(this.ref.find())); }
  click() { this.ref.trigger('click'); }
}
const cy = {};
cy.get = (selector) => new CyElement(selector);
// *** IGNORE: END SHIM ***

let target, button;

target = cy.get('.label_1').contains('Some text 1');
button = findCousin(target, 'button');
if (button != null) {
  button.click();
}

target = cy.get('.label_1').contains('Some text 2');
button = findCousin(target, 'button');
if (button != null) {
  button.click();
}

function findCousin(node, childSelector) {
  while (node != null & node.length > 0 && node.find(childSelector).length < 1) {
    node = node.parent();
  }
  return node ? node.find(childSelector) : null;
}

function handleClickEvent(button) {
  console.log(`Clicked on ${button.textContent}`);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="class_0">
  <div class="class_1"></div>
  <div class="class_2">
    <div class="label_0">
      <div class="label_1">Some text 1</div>
    </div>
  </div>
  <div class="class_3">
    <div class="tooltip_0"></div>
    <div class="tooltip_1">
      <form>
        <button onclick="handleClickEvent(this); return false;">Button 1</button>
      </form>
    </div>
    <div class="tooltip_2"></div>
  </div>
</div>

<div class="class_0">
  <div class="class_1"></div>
  <div class="class_2">
    <div class="label_0">
      <div class="label_1">Some text 2</div>
    </div>
  </div>
  <div class="class_3">
    <div class="tooltip_0"></div>
    <div class="tooltip_1">
      <form>
        <button onclick="handleClickEvent(this); return false;">Button 2</button>
      </form>
    </div>
    <div class="tooltip_2"></div>
  </div>
</div>

Here is a jQuery-only version

// *** IGNORE: BEGIN SHIM ***
(($) => {
  $.fn.findByContent = function(text) {
    return this.contents().filter((i, c) => $(c).text().trim() == text.trim());
  };
  $.fn.cousin = function(childSelector) {
    let node = this;
    while (node != null & node.length > 0 && node.find(childSelector).length < 1) {
      node = node.parent();
    }
    return node ? node.find(childSelector) : null;
  }
})(jQuery);

$('button').on('click', (e) => {
  console.log(`Clicked on ${$(e.target).text()}`);
  e.preventDefault();
})

let button1 = $('.label_1').findByContent('Some text 1').cousin('button');
if (button1 != null) {
  button1.click();
}

let button2 = $('.label_1').findByContent('Some text 2').cousin('button');
if (button2 != null) {
  button2.click();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="class_0">
  <div class="class_1"></div>
  <div class="class_2">
    <div class="label_0">
      <div class="label_1">Some text 1</div>
    </div>
  </div>
  <div class="class_3">
    <div class="tooltip_0"></div>
    <div class="tooltip_1">
      <form>
        <button>Button 1</button>
      </form>
    </div>
    <div class="tooltip_2"></div>
  </div>
</div>

<div class="class_0">
  <div class="class_1"></div>
  <div class="class_2">
    <div class="label_0">
      <div class="label_1">Some text 2</div>
    </div>
  </div>
  <div class="class_3">
    <div class="tooltip_0"></div>
    <div class="tooltip_1">
      <form>
        <button>Button 2</button>
      </form>
    </div>
    <div class="tooltip_2"></div>
  </div>
</div>

Upvotes: 1

Related Questions