'How to implement a command to get a table cell in Cypress?
I want to create a command with this interface:
cy.getTableCell({ column: 'Name', row: 42 }).contains('Douglas Adams')
Where getTableCell would return the table cell (td) corresponding to the 42-th row of the table on the column 'Name'. I came up with this implementation:
type GetTableCellParams = {
columnName: string;
rowIndex: number;
};
Cypress.Commands.add(
'getTableCell',
({ columnName, rowIndex }: GetTableCellParams) => {
cy.contains('th', columnName)
.invoke('index')
.then((index) => {
cy.get('tr')
.eq(rowIndex)
.within((row) => {
return cy.get('td').eq(index);
});
});
}
);
It does find the right table cell. However, since it does so inside a callback, I can't do anything with it - I would like to be able to call chainable methods such as contains, click, etc. How can I refactor this so the caller has access to this element, being able to call contains, click and other chainable methods?
I could also use some help on the function readability. It looks like a mess - I guess the problem lies with the nested callbacks...
Solution 1:[1]
It works with no returns at all.
Cypress uses a command stack, and the last subject on the stack is the value returned.
The problem with .within() is it reverts the subject after it finishes.
Cypress.Commands.add('getTableCell', ({ columnName, rowIndex }: GetTableCellParams) => {
cy.contains('th', columnName).invoke('index')
.then(colIndex => {
cy.get('tr').eq(rowIndex)
.find('td').eq(colIndex)
});
}
)
To illustrate, try aliasing the <td> and follow the .within() by getting the alias value
Cypress.Commands.add('getTableCell', ({ columnName, rowIndex }) => {
cy.contains('th', columnName)
.invoke('index')
.then(colIndex => {
cy.get('tr')
.eq(rowIndex)
.within((row) => {
cy.get('td').eq(colIndex).as('cell')
})
cy.get('@cell') // last command, it's result will be returned
});
}
)
Solution 2:[2]
Your command is almost working -- the main issue is that
.within()yields the same element it was given. Instead, we should use.find().You'll need to yield the entire Cypress chain to have the command chainable as you'd like. In this case, after subbing out
.within()for.find(), that means just adding areturnbefore the initialcy.contains()
Cypress.Commands.add(
'getTableCell',
({ columnName, rowIndex }: GetTableCellParams) => {
return cy.contains('th', columnName)
.invoke('index')
.then((index) => {
return cy.get('tr')
.eq(rowIndex)
.find('td')
.eq(index);
});
}
);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | agoff |
