Unable to write entire <tr> in Cypress test script - cypress

I am trying to write <tr> elements found in a table to an external file. But unable to save the data.
const rows = []
cy.get('tbody').eq(2).within(() => {
cy.get("tr").then((rows) => {
rows.toArray().forEach((element) => {
cy.log(element)
rows.push(element)
});
});
});
cy.log(rows)
cy.writeFile('cypress/fixtures/test.txt', rows)
The first cy.log() is printing the desired <tr> correctly. But the second cy.log() is printing the empty array. Also the file is empty. I also tried to write the element directly but no luck. Please advise what I am doing wrong.
Output of cy.log(element) :
<tr class="altRow row1" id="country_list">
<td>AAE</td> <td>ANNABA</td> <td>DZ</td> <td>ALGERIA</td>
</tr>

You can directly get the tr element using id. And then direct your search inside the same tr using within. Then using each() you can iterate over the td elements and then write the td inner texts(country names) in the txt file. The flag: a+makes sure that the new content is appended at the end of the file, so that with every write the file is not over-written.
cy.get('tr#country_list').within(() => {
cy.get('td').each(($ele) => {
cy.log($ele.text()) //Prints the country names one by one
cy.writeFile('cypress/fixtures/test.txt', $ele.text(), {flag: 'a+'}) //Adds the country in txt file
})
})

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

Bash script to add multiple lines to a file in alphabetical order

I have an html document which I want to edit from a bash script. The hmtl file has a table of entries formatted like this:
<table>
<tr>
<td>XXXX</td>
<td>XXXX</td>
</tr>
<tr>
<td>YYYY</td>
<td>YYYY</td>
</tr>
</table>
with each <tr> containing two <td>s with links to different versions of each thing.
What I need to do, from a bash script, add a new <tr> block into the table, in the correct alphabetical order according to whatever XXXX is.
I already have a larger script that adds entries to a bunch of other files via sed commands, but this file needs to be in alphabetical order, and I'm not sure how to approach this.
sed doesn't understand html. Use an html-aware tool, for example xsh, a wrapper around XML::LibXML:
open :F html file.html ;
my $new = "JJJJ" ;
my $after = //table/tr[xsh:strmax($new, preceding-sibling::tr/td/a) = $new][last()] ;
my $tr := insert element tr before $after ;
insert chunk {"
<td><a href='$new-1.example.com'>$new</a></td>
<td><a href='$new-2.example.com'>$new</a></td>
"} into $tr ;
Note the XPath that locates the tr after which the new one should be inserted: it's the last tr such that a max string from all its preceding sibling tr's plus the new string is equal to the new string. The following tr will have the string greater than the new one, so it would change the maxstr. (The code doesn't handle the case when the new string should come first, in such a case, $after would be empty).

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]")

Parse a HTML table using Ruby, Nokogiri omitting the column headers

I have trouble parsing a HTML table using Nokogiri and Ruby. My HTML table structure looks like this
<table>
<tbody>
<tr>
<td>Firstname</td>
<td>Lastname</td>
<td>Middle</td>
</tr>
<tr>
<td>ding</td>
<td>dong</td>
<td>ling</td>
</tr>
....
....
.... {more tr's and td's with similar data exists.}
....
....
....
....
....
</tbody>
</table>
In the above HTML table I would like to entirely remove the first and corresponding elements, so remove Firstname, Lastname and Middle i.e., I want to start stripping the text only from the second . So this way I get only the contents of the table from the second or tr[2] and no column headers.
Can someone please provide me a code as to how to do this.
Thanks.
require 'rubygems'
require 'nokogiri'
doc = Nokogiri::HTML(x)
rows = doc.xpath('//table/tbody/tr[position() > 1]')
# OR
rows = doc.xpath("//table/tbody/tr")
header = rows.shift
After you've run either one of the above 2 snippets, rows will contain every <tr>...</tr> after the first one. For example puts rows.to_xml prints the following:
<tr><td>ding</td>
<td>dong</td>
<td>ling</td>
</tr>
To get the inner text, removing all the html tags, run puts rows.text
ding
dong
ling
To get the inner text of the td tags only, run rows.xpath('td').map {|td| td.text }
["ding", "dong", "ling"]
Alternatively:
table.css('tr')[1..-1]
or to strip out the text starting at row 2:
table.css('tr')[1..-1].map{|tr| tr.css('td').map &:text}
Since Nokogiri does support :has CSS pseudo-class you can get heading row with
#doc.at_css('table#table_id').css('tr:has(th)')
and since it does supports :not CSS pseudo-class as well, you can get other rows with
#doc.at_css('table#table_id').css('tr:not(:has(th))')
respectively. Depending on your preferences you might like to avoid negation and just use css('tr:has(td)').

Resources