Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Events and cy.get

Tags:

cypress

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

like image 613
RhythmicDevil Avatar asked Sep 03 '25 14:09

RhythmicDevil


1 Answers

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.

like image 50
dwelle Avatar answered Sep 05 '25 14:09

dwelle