javascriptsinoncypressend-to-endprintjs

How to stub contentwindow.print with cypress/test printJS with cypress


My program uses printJS which a library that helps format the content of the page for printing. I wanted to write tests with cypress to test that the print preview has been called. Currently I have a button that calls printJS when clicked and since cypress cannot interact with the print preview window, I thought it would be a good idea to stub the call to printJS then write an assertion that it was called once. I understand that this works with window.print() as you can stub that with this code.

cy.visit('http://127.0.0.1/',{
    onBeforeLoad: (win) => {
        cy.stub(win, 'print')
    }
})

Then assert with this

cy.contains('print').click()
cy.window().then((win) => {
    expect(win.print).to.be.calledOnce
})

My old button

<button type="button" class="btn btn-secnodary" onclick="window.print()">
    Print
</button>

But instead I used printJS which means my button now looks like this

<button type="button" onclick="printJS({printable: 'id_preview_modal_body', type: 'html'})" data-dismiss="modal">
    Print
</button>

The javascript gets loaded in as print.min.js which can be found here. I tried to stub the contentwindow but that doesn't seem to work so far. In the code for printJS, the printing happens here

frameElement.contentWindow.print()

from their github page, line 63

The way im stubbing it gives this issue

cy.visit('http://127.0.0.1:8000/notices/new/',{
    onBeforeLoad: (win) => {
        cy.stub(win, 'printJS')
    }
})

Uncaught TypeError: Cannot stub non-existent own property printJS

The assertion also gives this error

cy.window().then((win) => {
    expect(win.printJS).to.be.calledOnce
})

TypeError: [Function: init] is not a spy or a call to a spy!

I think the [Function: init] is a referring to const printJS = print.init from their index.js file. But i don't know how to proceed further in debugging this issue. Any help would be appreciated. Thanks!


Solution

  • The problem is the onBeforeLoad hook is called before printJS is initiated, when printJS is imported it invokes it's init() function and overwrites your stub in window.print.

    This is stubbing too soon

    cy.visit('http://127.0.0.1:8000/notices/new/',{
        onBeforeLoad: (win) => {
            cy.stub(win, 'printJS')
        }
    })
    

    Stubbing after component has loaded and printJS is initiated

    const printStub
    
    before(function(){
    
      cy.visit('http://127.0.0.1:8000/notices/new/')
    
      // maybe wait for loading to complete
    
      cy.window().then(win => {
        printStub = cy.stub(win, 'printJS')
      })
    })
    
    it('stubs printJS', () => {
      cy.contains('button', 'Print').click()
      cy.window().then(win => {
        expect(printStub).to.be.calledOnce
      })
    })