Reputation: 193
I've received a Cypress error suggesting that multiple elements contain the searched value, so it cannot recognize the requested one.
Here is the Cypress script I've used for testing:
it('Check all promotions', () => {
// Check Promotion 1
let promotionCode = 'UK_ZYN_MULTI_BUY_20';
let promotionName = '10 ZYN Cans multi-buy + 10 ZYN Cans multi-buy';
let websiteEnv = 'Inactive';
let promotionVersion = '2';
searchPromotion(promotionCode, promotionName, websiteEnv, promotionVersion);
});
});
// searchPromotion function
function searchPromotion(promotionCode, promotionName, websiteEnv, promotionVersion) {
cy.get('.z-bandbox-input')
.eq(6)
.should('exist')
.clear()
.type(promotionCode, { force: true })
.wait(300);
cy.get('.yw-textsearch-searchbutton.z-button')
.eq(1)
.click({ force: true })
.wait(10000);
const selector = "tr.yw-coll-browser-hyperlink:has(.yw-listview-cell-label.z-label:contains(\"" + promotionCode + "\")):has(.yw-listview-cell-label.z-label:contains(\"" + promotionName + "\")):has(.yw-listview-cell-label.z-label:contains(\"" + websiteEnv + "\")):has(.yw-listview-cell-label.z-label:contains(\"" + promotionVersion + "\"))";
function checkPage() {
cy.get('body').then($body => {
if ($body.find(selector).length > 0) {
cy.get(selector).within(() => {
cy.get('.yw-listview-cell-label.z-label')
.filter((_, el) => {
// Ensure the version is an exact match
const versionText = el.innerText.trim();
return versionText === promotionVersion; // Exact match check
})
.first() // Ensure only one element is selected
.click({ force: true });
});
} else {
cy.get('button.z-paging-button.z-paging-next[title="Next Page"]').eq(2).then($button => {
if ($button.is(':visible') && !$button.attr('disabled')) {
cy.wrap($button).click({ force: true }).wait(5000);
checkPage(); // Recursively check the next page
} else {
cy.log('Promotion not found or next page button is not clickable');
}
});
}
});
}
checkPage();
}
Cypress searched for the promotions and there is 3 of them. Basically those are 3 versions of the same promotion, so only their version number is different, everything else is the same, including the class name of each field.
In order to comprehend it better: When promotions are searched they are listed with their Code, Name, Status, Version etc. All these fields contains the same class name. When you search for the one promotion in particular by its code, you get a list of various versions of that promotion. It means that only the version value differs, and by that value Cypress can determine which promotion should it choose for further testing.
I've solved that by implementing this const:
const selector = "tr.yw-coll-browser-hyperlink:has(.yw-listview-cell-label.z-label:contains(\"" + promotionCode + "\")):has(.yw-listview-cell-label.z-label:contains(\"" + promotionName + "\")):has(.yw-listview-cell-label.z-label:contains(\"" + websiteEnv + "\")):has(.yw-listview-cell-label.z-label:contains(\"" + promotionVersion + "\"))";
Still, it seems that my functions didn't solve the problem of searching for exact values.
What I believe is the issue here is that Cypress is mistaking the "2" value set for the version field with the number "2" that is appearing in each promotion code: UK_ZYN_MULTI_BUY_20
I thought that the function I've set in my script could handle this, but it seems that it is missing something additional.
Upvotes: 1
Views: 88
Reputation: 150
You are not using $body
but if you change it to tbody
ie. the body of the table, you can then .filter()
rows for the correct cell contents.
Adding :eq(cell-number)
overcomes the issue of finding promotionVersion
in the wrong column.
Using string template for values makes the :contains()
shorter.
cy.get('tbody').then($tbody => {
const target = $tbody.find(tr)
.filter(`:has(td:eq(0):contains("${promotionCode}"))`)
.filter(`:has(td:eq(1):contains("${promotionName}"))`)
.filter(`:has(td:eq(2):contains("${websiteEnv}"))`)
.filter(`:has(td:eq(3):contains("${promotionVersion}"))`)
if (target.length) {
cy.wrap($el).click({ force: true })
} else {
// next page logic
}
});
Simple version of the table for checking:
<table>
<tbody>
<tr>
<td>a</td>
<td>b</td>
<td>c2</td>
<td>0</td>
</tr>
<tr>
<td>a</td>
<td>b</td>
<td>c2</td>
<td>1</td>
</tr>
<tr>
<td>a</td>
<td>b</td>
<td>c2</td>
<td>2</td>
</tr>
</tbody>
</table>
Testing:
const promotionCode = 'a'
const promotionName = 'b'
const websiteEnv = 'c2'
const promotionVersion = '2'
it('finds the above criteria', () => {
cy.get('tbody').then($tbody => {
const target = $tbody.find('tr')
.filter(`:has(td:eq(0):contains("${promotionCode}"))`)
.filter(`:has(td:eq(1):contains("${promotionName}"))`)
.filter(`:has(td:eq(2):contains("${websiteEnv}"))`)
.filter(`:has(td:eq(3):contains("${promotionVersion}"))`)
if (target.length) {
cy.log(`found at row ${target.index()}`)
} else {
cy.log('not found')
}
})
});
Passes:
Upvotes: 5
Reputation: 193
After keep trying I've managed to solve the issue by implementing this script:
// Loop through pages to find the promotion
function checkPage() {
cy.get('body').then($body => {
// Define a more precise selector for the rows containing promotion details
cy.get('tr.yw-coll-browser-hyperlink').each(($row) => {
// Ensure that the row contains all necessary values except for the version first
if (
$row.find('.yw-listview-cell-label.z-label:contains("' + promotionCode + '")').length > 0 &&
$row.find('.yw-listview-cell-label.z-label:contains("' + promotionName + '")').length > 0 &&
$row.find('.yw-listview-cell-label.z-label:contains("' + websiteEnv + '")').length > 0) {
// Check for the correct version specifically, using each() instead of filter()
cy.wrap($row).within(() => {
cy.get('.yw-listview-cell-label.z-label').each(($el) => {
const versionText = $el.text().trim();
const className = $el.attr('class'); // Get the class name of the element
// Ensure the class and the version both match exactly
if (className.includes('z-label') && versionText === promotionVersion) {
cy.wrap($el).click({ force: true }); // Click only if it matches the correct version
}
});
});
}
});
// Handle pagination if necessary
cy.get('button.z-paging-button.z-paging-next[title="Next Page"]').eq(2).then($button => {
if ($button.is(':visible') && !$button.attr('disabled')) {
cy.wrap($button).click({ force: true }).wait(5000);
checkPage(); // Recursively check the next page
} else {
cy.log('Promotion not found or next page button is not clickable');
}
});
});
}
checkPage();
}
Upvotes: 0
Reputation: 154
I guess in the absence of attributes other than class
(maybe aria-label?) you want exact text match.
Since there is a tr
it's a table, you don't really need the classes in the selector, you can use the td
within the tr
. Using contains('td', ...)
you want an exact match, so a regex text value would do this.
In the regex ^
is start and $
is end, if you add front and back it excludes the larger string that has 2
in the middle.
cy.get('table#my-table') // identify the table
.contains('td', new RegExp(`^${promotionVersion}$`) // cell with version
.parent('tr') // it's row
I'm not sure how to fit that into the selector
const, maybe :nth-child()
does it, but it's getting harder to "know" your selector is correct with more clauses in it.
The test is much easier to follow when it's simple, so I would just break it down with smaller steps.
Upvotes: 4