Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mocking the print command without mocking the module #782

Open
charlieforward9 opened this issue Jan 29, 2025 · 2 comments
Open

mocking the print command without mocking the module #782

charlieforward9 opened this issue Jan 29, 2025 · 2 comments
Labels

Comments

@charlieforward9
Copy link

charlieforward9 commented Jan 29, 2025

I cannot use vi.mock in my browser based tests because it will override my msw network interceptors.

Because of this, I cannot mock this module, leading to the print dialog popping up in my browser tests, causing my tests to hang. Locally, I can close the dialog, but in my CI pipelines, there is no way for me to close them, leading to never-ending tests.

In my test setup file, I have tried:

beforeAll(async () => {
  const print = vi.fn(() => void 0);
  vi.stubGlobal("print", print);

  window.print = print;

  //FIXME: https://github.com/mswjs/msw/issues/2398
  // vi.mock("react-to-print", () => ({
  //   useReactToPrint: vi.fn(() => vi.fn()),
  // }));
});

but (seemingly) because this repo does not directly use window.print(), it is not working as expected.

printWindow.contentWindow.print();

I would like to know if there is any recommended workaround to this that I can employ without modifying my source code?

Thank you.

Parent Issues: mswjs/msw#2398 , vitest-dev/vitest#6766 , vitest-dev/vitest#7290

@MatthewHerbst
Copy link
Owner

MatthewHerbst commented Jan 29, 2025

Hello. Interesting problem.

because this repo does not directly use window.print

We absolutely do use window.print:

printWindow.contentWindow.print();

What might be tripping you up here is that the window.print that you need to mock is the one inside of the iframe that react-to-print temporarily creates. I would have assumed that the iframe inherits the window.print method from its parent, however, I just tested that locally and can confirm that, in Chrome 131 at least, it does not!

I'm not a vitest expert, but if you can figure out how to get it to mock the window.print in all, or at least child, windows that are created, that would likely solve your problem.

@MatthewHerbst
Copy link
Owner

I asked an LLM how to do this. Here is that conversation. Seems a bit hacky, but also like it might work? I have not tested this:


Matt
I have a javascript program that creates an iframe on a page. I'm writing some tests for my program with vitest. In my tests I'd like to mock the window.print method. However, when I do this, it only mocks the window.print method on the parent page, but not on the iframe that gets created while the program is running.

How can I ensure all child iframes of my page inherit the mocked window.print method?

LLM
To mock the window.print method for all child iframes created by your JavaScript program during tests with Vitest, you'll need to intercept iframe creation and override the print method on each iframe's window object. Here's a general approach on how to achieve this:

  1. Use a global before hook to override the default behavior of creating iframes.
  2. Replace the native document.createElement method temporarily to catch the creation of iframes.
  3. When an iframe is created, wait for it to load and then mock its window.print method.

Here's an example of how this might look in your Vitest test:

Note: This is a simplified example and may need to be adjusted based on your specific setup and requirements. The key is to intercept the iframe creation and modify its window object before your program has a chance to call its print method.

import { beforeAll, describe, it, expect, vi } from 'vitest';

// Mock for window.print
const mockPrint = vi.fn();

beforeAll(() => {
  // Store the original createElement method
  const originalCreateElement = document.createElement;

  // Override the createElement method to intercept iframe creation
  document.createElement = function (tagName) {
    const element = originalCreateElement.call(document, tagName);

    if (tagName.toLowerCase() === 'iframe') {
      // Once the iframe is added to the document, override its print method
      element.addEventListener('load', () => {
        element.contentWindow.print = mockPrint;
      });
    }

    return element;
  };
});

describe('iframe tests', () => {
  it('should mock print method on iframe', async () => {
    // Your test code that creates an iframe
    // ...

    // After your iframe is created and loaded, you can assert that the mockPrint function was called
    // Call the print method on the iframe to test
    // iframe.contentWindow.print();

    // Check if the mocked function was called
    expect(mockPrint).toHaveBeenCalled();
  });
});

// Remember to restore the original createElement method after your tests
afterAll(() => {
  document.createElement = originalCreateElement;
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants