Cypress selector with closest not working as expected - cypress

I can not select the proper select element which matches the label ('Special Class 1'). I have a couple of select items on the same web page with different labels but a similar structure like this:
<table>
<tbody><tr>
<td>
<label>
Special Class 1
</label>
</td>
<td><div>
<select>
<option>One</option>
</select></div></td>
</tr></tbody>
</table>
My code looks like this:
cy.contains('table', 'Special Class 1').should('be.lengthOf', 1)
.closest('tr').get('select')
.select('One');
But I always get the following since I have 5 follow up select elements (like the ones above):
cy.select() can only be called on a single <select>. Your subject contained 5 elements.Learn more
cypress/support/jsf/JsfWizard.ts:23:8
21 | cy.contains('table[class]', propertyLabel).should('be.lengthOf', 1)
22 | .closest('tr').get('select')
> 23 | .select(option);
| ^
I tried already lots of things, also with parentsUntil. This is how I understood closest(). Any hints?

What happens if you:
21 | cy.contains('table[class]', propertyLabel).should('be.lengthOf', 1)
22 | .closest('tr').get('select').then(select => {
console.log(select)
})
Does it print multiple elements?
Maybe you can target the element by index afterwards? Or
maybe you can have an object with the options
{object: {option1: "abc", etc} }
and select the element with the key afterwards object[option1]?

This .get('select') should be .find('select').
The get command will always start searching from the root element <body>, the find command starts searching at the last subject, the <tr>.
cy.contains('table', 'Special Class 1')
.should('be.lengthOf', 1)
.closest('tr')
.get('select') // start search over, ignore previous element
.select('One');
})
cy.contains('table', 'Special Class 1')
.should('be.lengthOf', 1)
.closest('tr')
.find('select') // search inside previous element
.select('One');
})
Or use .within()
cy.contains('table', 'Special Class 1')
.should('be.lengthOf', 1)
.closest('tr').within(() => {
cy.get('select')
.select('One');
})

Related

Cypress: Finding the content of a specific td element

I need to find the content of a specific td element based on the content of the next td element in the row.
The markup (simplified):
<table id="oversiktOverSoknaderTable">
<tr data-e2e-selector="soknad-linje">
<td data-e2e-selector="status">Trukket</td>
<td data-e2e-selector="soknadProsjektNavn">Endret søknad</td>
<td>Lån</td>
<td data-e2e-selector="soknadId" id="2"></td>
</tr>
<tr data-e2e-selector="soknad-linje">
<td> ...
There can be multiple rows, all with the same selector (data-e2e-selector="soknad-linje">. The issue at hand is to verify that the correct status is showing for the correct name. (Sorry for not translating the selector names, but I think they're quite self-explanatory.)
What I want to do, is to use .should('contain'.'value') to verify that the text in the data-e2e-selector="status" quals "Trukket" for the line where the data-e2e-selector="soknadProsjektNavn" is "Endret søknad". (As it is in the above example.)
Ideas?
Something like this, you can do
cy.contains('td', 'Endret søknad')
.sibling('[data-e2e-selector="status"]')
.should('contain', 'Trukket')
where sibling() takes a selector to specify which one.
There is also .prev() command.
Like #jjhelguero I would also recommend being specific with data-e2e-selector in the selector.
cy.contains('td[data-e2e-selector="soknadProsjektNavn"]', 'Endret søknad')
.prev('td[data-e2e-selector="status"]')
.should('have.text', 'Trukket')
Assuming Endret søknad is a unique value in your table.
Since you have data-e2e-selector in your elements you can use the follow
cy.get('#oversiktOverSoknaderTable') // hopefully this is unique enough
.should('be.visible') // assertion to avoid element detached from DOM
cy.contains('[data-e2e-selector="soknad-linje"]', 'Endret søknad') // gets row with unique string
.should('be.visible')
.find('[data-e2e-selector="status"]') // searches in row for element
.should('have.text', 'Trukket') // has status text
If you're getting detached from DOM error, try keeping the commands preceding .should() (which triggers retry) as short as possible.
cy.contains('[data-e2e-selector="soknad-linje"]', 'Endret søknad') // row with project
.should('contain', 'Trukket') // confirm the status
or
cy.get('[data-e2e-selector="soknad-linje"]')
.should('contain', 'Endret søknad')
.should('contain', 'Trukket')
You can do something like this:
cy.contains('td', 'Endret søknad')
.parent('tr')
.within(() => {
cy.get('[data-e2e-selector="status"]').should('have.text', 'Trukket')
})

Idiomatic Cypress way to assert that two elements that contains some text exist in the DOM

Using Cypress, what is the idiomatic way to assert that two elements exist in the DOM for a given selector (that also contain some text)?
Here's how I would do that in JavaScript:
Array.from(document.querySelectorAll("selector")).filter(node => node.textContent.includes("text")).length === 2
Is there an idiomatic way to do this in Cypress?
I've tried:
cy.get('selector')
.contains('text')
.should('have.length', 2);
but I'm getting the following error:
cy.contains() cannot be passed a length option because it will only ever return 1 element.
You can use filter() in combination with contains() like this. (Cypress Docs)
cy.get('selector')
.filter(':contains("text")')
.should('have.length', 2);
Alternatively, you can use the below approach without the 'filter' option.
Example:
<table>
<tbody>
<tr>
<td>Same</td>
<td>Same</td>
<td>Different</td>
<td>Same</td>
</tr>
</tbody>
</table>
// selects all table cells with text "Same"
cy.get('td:contains("Same")').should('have.length', 3)
// if the text does not have white spaces, no need to quote it
cy.get('td:contains(Same)').should('have.length', 3)
// you can find elements NOT having the given text
cy.get('td:not(:contains(Same))')
.should('have.length', 1)
.and('have.text', 'Different')
Read more at here

CKEditor Plugin - Proper behavior of elementPath

Currently, I have the following HTML content
<span criteria="{"animal":["DOG"]}">abc</span> def <span criteria="{"animal":["CAT"]}">ghi</span>
My purpose is
I wish to know my selected text contain criteria attribute?
If it contains criteria attribute, what is its value?
I run the following code.
editor.on('selectionChange', function( ev ) {
var elementPath = editor.elementPath();
var criteriaElement = elementPath.contains( function( el ) {
return el.hasAttribute('criteria');
});
var array = elementPath.elements;
var arrayLength = array.length;
for (var i = 0; i < arrayLength; i++) {
console.log(i + " --> " + array[i].$.innerHTML);
}
if (criteriaElement) {
console.log("criteriaElement is something");
console.log("criteriaElement attribute length is " + criteriaElement.$.attributes.length);
for (var i = 0; i < criteriaElement.$.attributes.length; i++) {
console.log("attribute is " + criteriaElement.$.attributes[i].value);
}
}
});
Test Case 1
When I select my text abc def as follow
I get the following logging
0 --> abc
1 --> <span criteria="{"animal":["DOG"]}">abc</span> def <span criteria="{"animal":["CAT"]}">ghi</span>
criteriaElement is something
criteriaElement attribute length is 1
attribute is {"operator":["DOG"]}
Some doubts in my mind.
I expect there will be 2 elements in elementPath. One is abc, another is def. However, it turns out, my first element is abc (correct), and my second element is the entire text (out of my expectation)
Test Case 2
I test with another test. This time, I select def ghi
I get the following logging
0 --> <span criteria="{"animal":["DOG"]}">abc</span> def <span criteria="{"animal":["CAT"]}">ghi</span>
Some doubts in my mind
Why there is only 1 element? I expect there will be 2 elements in elementPath. One is def, another is ghi.
Although Test Case 1 and Test Case 2 both contain element with entire text, why in Test Case 2, elementPath.contains... returns nothing?
Elementspath is not related to the selection in that way. It represent the stack of elements under the the caret. Imagine a situation like this where [] represents the selection and | represents the caret:
<ul>
<li>Quux</li>
<li>F[oo <span class="bar">Bar</span> <span class="baz">Ba|]z</span></li>
<li>Nerf</li>
</ul>
Your selection visually contains the text "oo Bar Ba" and your caret is in between a and z. At that time, the elementspath would display "ul > li > span". The other span element "bar" is a sibling of the span element "baz" and is thus not displayed, only ascendants are displayed.
You could think of it like that the caret can only exist inside a html TEXT_NODE and the elementspath displays the ascendants of that text node.
What are you trying to eachieve? To display the data in the current selection? Why? Where do you want it to show? How and why do you want it to show? I'm guessing that there is a different way of fillind the requirement that you have than with using the elementspath (I'm think this might be and XY problem).
Too long to be a comment: If your toolbar button action targets elements with the criteria attribute - what if there is one span with a criteria attribute and 1 without? Does their order matter? What if there are two spans with a criteria attribute? What if they are nested like this: <p>F[oo <span criteria="x">Bar <span criteria="y">Ba|]z </span>Quux </span>Xyzzy</p> - the targeting will be difficult. I would suggest that you add a small marker to the elementspath if an element has the attribute, than clicking the marker or rightclicking the element you could edit/view the criteria. You could even visually indicate spans with the attribute within the editor by customizing editor.css with a rule like span[criteria]{ color: red; }.

accessing the text value of last nested <tr> element with no id or class hooks

I need to access the value of the 10th <td> element in the last row of a table. I can't use an ID as a hook because only the table has an ID. I've managed to make it work using the code below. Unfortunately, its static. I know I will always need the 10th <td> element, but I won't ever know which row it needs to be. I just know it needs to be the last row in the table. How would I replace "tr[6]" with the actual last <tr> dynamically? (this is probably really easy, but this is literally my first time doing anything with ruby).
page = Nokogiri::HTML(open(url))
test = page.css("tr[6]").map { |row|
row.css("td[10]").text}
puts test
You want to do:
page.at("tr:last td:eq(10)")
If you do not need to do anything else with the page you can actually make this a single line with
test = Nokogiri::HTML(open(url)).search("tr").last.search("td")[10].text
Otherwise (this will work):
page = Nokogiri::HTML(open(url))
test = page.search("tr").last.search("td")[10].text
puts test
Example:(Used a large table from another question on StackOverflow)
Nokogiri::HTML(open("http://en.wikipedia.org/wiki/Richard_Dreyfuss")).search('table')[1].search('tr').last.search('td').children.map{|c| c.text}.join(" ")
#=> "2013 Paranoia Francis Cassidy"
Is there a particular reason you want an Array with 1 element? My example will return a string but you could easily modify it to return an Array.
You can use CSS pseudo class selectors for this:
page.css("table#the-table-id tr:last-of-type td:nth-of-type(10)")
This first selects the <table> with the appropriate id, then selects the last <tr> child of that table, and then selects the 10th <td> of that <tr>. The result is an array of all matching elements, if youexpect there to be only one you could use at_css instead.
If you prefer XPath, you could use this:
page.xpath("//table[#id='the-table-id']/tr[last()]/td[10]")

how to click a link in a table based on the text in a row

Using page-object and watir-webdriver how can I click a link in a table, based on the row text as below:
The table contains 3 rows which have names in the first column, and a corresponding Details link in columns to the right:
DASHBOARD .... Details
EXAMPLE .... Details
and so on.
<div class="basicGridHeader">
<table class="basicGridTable">ALL THE DETAILS:
....soon
</table>
</div>
<div class="basicGridWrapper">
<table class="basicGridTable">
<tbody id="bicFac9" class="ide043">
<tr id="id056">
<td class="bicRowFac10">
<span>
<label class="bicDeco5">
<b>DASHBOARD:</b> ---> Based on this text
</label>
</span>
</td>
<td class="bicRowFac11">
....some element
</td>
<td class="bicRowFac12">
<span>
<a class="bicFacDet5">Details</a> ---> I should able click this link
</span>
</td>
</tr>
</tbody>
</table>
</div>
You could locate a cell that contains the specified text, go to the parent row and then find the details link in that row.
Assuming that there might be other detail links you would want to click, I would define a view_details method that accepts the text of the row you want to locate:
class MyPage
include PageObject
table(:grid){ div_element(:class => 'basicGridWrapper')
.table_element(:class => 'basicGridTable') }
def view_details(label)
grid_element.cell_element(:text => /#{label}/)
.parent
.link_element(:text => 'Details')
.click
end
end
You can then click the link with:
page.view_details('DASHBOARD')
Table elements include the Enumerable module, and I find it very useful in cases like these. http://ruby-doc.org/core-2.0.0/Enumerable.html. You could use the find method to locate and return the row that matches the criteria you are looking for. For example:
class MyPage
include PageObject
table(:grid_table, :class => 'basicGridTable')
def click_link_by_row_text(text_value)
matched_row = locate_row_by_text(text_value)
matched_row.link_element.click
#if you want to make sure you click on the link under the 3rd column you can also do this...
#matched_row[2].link_element.click
end
def locate_row_by_text(text_value)
#find the row that matches the text you are looking for
matched_row = grid_table_element.find { |row| row.text.include? text_value }
fail "Could not locate the row with value #{text_value}" if matched_row.nil?
matched_row
end
end
Here, locate_row_by_text will look for the row that includes the text you are looking for, and will throw an exception if it doesnt find it. Then, once you find the row, you can drill down to the link, and click on it as shown in the click_link_by_row_text method.
Just for posterity, I would like to give an updated answer. It is now possible to traverse through a table using table_element[row_index][column_index].
A little bit more verbose:
row_index could also be the text in a row to be matched - in your case - table_element['DASHBOARD']
Then find the corresponding cell/td element using either the index(zero based) or the header of that column
table_element['DASHBOARD'][2] - Selecting the third element in the
selected row.
Since you do not have a header row (<th> element) you can filter the cell element using the link's class attribute. Something like this
table_element['DASHBOARD'].link_element(:class => 'bicRowFac10').click
So the code would look something like this:
class MyPage
include PageObject
def click_link_by_row_text(text_value)
table_element[text_value].link_element(:class => 'bicRowFac10').click
end
end
Let me know if you need more explanation. Happy to help :)

Resources