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...
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
});
}
)