I've been working on a Svelte project that integrates with an auth server. If the user's session cookie expires, the app shows a modal to the user prompting them to login. The modal persists until the user logs in. Creating this modal was easy. Unit testing the modal was not.
This article is all about writing unit tests for the modal using real timers.
Write some Tests
Start with the describe block:
Before jumping into the test, let's add an afterEach() block:
The afterEach() block will reset all Mocks, and run a clean up script which scrubs the virtual DOM, so every test starts fresh.
Then, start your test:
2})
First step in the test is to render the modal. We're going to render it as an open modal:
I am pulling two functions out of the render object, getByText(), and queryByRole().
First, we'll use getByText() to grab the modal's button:
And then we'll click the button:
This should trigger the button handler inside the modal, which starts the interval countdown to close the modal. Next, we have to increment the timer:
This should close the Modal. So, we can use the queryByRole() method to get the Modal and validate whether it is in the document--or not:
In practice this should work! In reality, it doesn't yet. You'll see an error like this:
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
Or a screenshot:

If you step through the code, you'll see that the final assertion that checks whether or not the dialog is closed executes before the method inside the interval that toggles the close. Even with a longer timeout, the test will still fail.
After much experimentation, and stepping through code I discovered that the issue lies with the animation to remove the dialog. We can solve that by spying on the animations. I'm going to create a method, which will mock the animate method, along with some of the modal methods:
2 Element.prototype.animate = vi.fn().mockImplementation(()=> ({
3 cancel: () => Promise.resolve(undefined)
4 }))
5 HTMLDialogElement.prototype.showModal = vi.fn().mockImplementation(() => {
6 Promise.resolve();
7 })
8 HTMLDialogElement.prototype.close = vi.fn().mockImplementation(() => {
9 Promise.resolve();
10 })
11}
Add a call to this method as the first line in the unit test:
We need to do one more thing. I dug out the actual Modal Code to discover it uses the Svelte fade transition. We want to mock this so that the transition is immediate, with no delays or duration:
This mock is added at the top of the test file, outside of the describe() statement. Now, re run the tests:

Success!
Final Thoughts
It took me four days and a lot of errors, even with the help of AI tools, to determine what mocks needed to be set up to get this test working.
Along the way I also created a version of this test using fake timers. That will be the topic of my next post.
Be sure to check out my Blog's repo for samples and follow along.
