I need some help on how to handle the following situation. There is a modal that when closed sends an AJAX request, and if the response is successful a Datatables table is redrawn. I need to test the content of the table after the draw.dt event is fired.
I get a reference to the table and then setup a handler for the draw.dt event. When the event fires cypress reports that cy.get cannot be run outside of a test. I suppose that the test is actually finished by the time this event gets processed.
What is the correct way to handle this situation?
cy.get('#commChannelModal > .modal-dialog > .modal-content > .modal-footer > .btn-primary')
.contains(this.edit_controller_interface_data.modals.edit_comm_channel.buttons.save.text)
.click({force: true}).then(function (){
cy.wait('@fetchComms').then(function () {
cy.get('#commChannelsTable').then(($table) => {
$table.on('draw.dt', function () {
// Verify in the index if the edited values are saved
cy.get('#commChannelsTable').get('tbody > tr:nth-child(3) > td:nth-child(1)')
.contains(this.edit_controller_interface_data.modals.edit_comm_channel.fields.connectionType.value)
cy.get('#commChannelsTable').get('tbody > tr:nth-child(3) > td:nth-child(2)')
.contains(this.edit_controller_interface_data.modals.edit_comm_channel.fields.ipAddress.value)
cy.get('#commChannelsTable').get('tbody > tr:nth-child(3) > td:nth-child(3)')
.contains(this.edit_controller_interface_data.modals.edit_comm_channel.fields.ipPort.value)
})
})
})
})
https://docs.cypress.io/guides/references/error-messages.html#Cypress-cannot-execute-commands-outside-a-running-test
You're right in your assessment that cypress is already done by the time your event fires. That's because attaching an event listener in itself doesn't signal to cypress it should wait for it to trigger (unlike e.g. in Node where attaching a listener signals to the process to not exit the script).
But, in most cases you don't need to wait for an event. Simply enqueue a DOM command --- cypress will wait (by default 4s) until the DOM matches your expectation. If you need to wait more, pass custom timeout
:
describe('test', () => {
it('test', () => {
cy.window().then(win => {
win.redraw = () => {
setTimeout(() => {
win.document.body.innerHTML = `
<div class="item">1</div>
`;
}, 5000 );
};
win.document.body.innerHTML = `
<div class="item">0</div>
<button class="redraw" onclick="redraw()">redraw</button>
`;
});
cy.get('.item').should('contain', '0');
cy.get('.redraw').click();
cy.get('.item', { timeout: 6000 }).should('contain', '1');
});
});
If there's something more going on that I don't understand, or you simply insist on waiting for the event to fire, you could cy.wrap
a promise and resolve it when the event fires:
describe('test', () => {
it('test', () => {
cy.window().then(win => {
win.redraw = () => {
setTimeout(() => {
win.document.body.innerHTML = `
<div class="item">1</div>
`;
win.document.dispatchEvent(
new win.CustomEvent('custom-event')
);
}, 5000 );
};
win.document.body.innerHTML = `
<div class="item">0</div>
<button class="redraw" onclick="redraw()">redraw</button>
`;
});
cy.get('.item').should('contain', '0');
cy.get('.redraw').click();
cy.wrap(new Promise(resolve => {
cy.document().then(doc => {
doc.addEventListener('custom-event', resolve);
});
})).then(() => {
cy.get('.item').should('contain', '1');
});
});
});
I'm dispatching a custom event just for demonstration purposes. You can use the draw.dt
event.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With