Search code examples
sveltejsdomvitesttesting-librarysvelte-testing-library

Unit testing code that fires at the end of the intro animation in Svelte


I created a Svelte component that is meant to show quickly and then disappear, merely to communicate that the action took place and how many records were touched by the action.

The component is very simple, and here is a simplified version:

<script lang="ts">
    import { type ShowStore } from "my-library";
    import { fade, blur } from 'svelte/transition';

    export let text: string;
    export let showStore: ShowStore;
    export let timeout = 500;

    function setExit() {
        console.log('setExit executed.');
        setTimeout(() => showStore.hide(), timeout);
    }
</script>

<div
    class="rounded-5 display-2 p-5 translate-middle fw-bold top-50 start-50"
    in:blur={{ duration: 200 }}
    out:fade
    on:introend={setExit}
>
    {text}
</div>

The CSS classes seen there are Bootstrap and are meant to show the element in the middle of the screen.

Now, using Vitest, I would like to write a unit test that makes sure the component hides itself after the specified timeout via the timeout prop. This does not seem to be working. The console.log() line seen inside the setExit() function doesn't happen, which makes me question if, while in the test environment, transitions happen: If setExit() is not being called, then intro animation is not finishing, right?

Before I show the unit test, I'll say this much about ShowStore: It is a utility Svelte store that holds a Boolean value and has the specialized methods show(), hide(), and toggle(). Other than that, is a Svelte store created with writable<boolean>().

The unit test looks like this:

    test('Should hide itself after the specified timeout value.', async () => {
        // Arrange.
        const text = '+123';
        const store = showStore();
        store.show();
        const timeout = 300;
        render(SwiftScore, { text, showStore: store, timeout });
        const spy = vi.spyOn(store, 'hide');

        // Act.
        await vi.waitFor(() => {
            console.log('Show value: %s', get(store));
            if (get(store)) {
                return Promise.reject();
            }
        }, timeout + 300);

        // Assert.
        expect(spy).toHaveBeenCalledOnce();
        expect(get(store)).toEqual(false);
    });

I am using @testing-library/svelte, jsDom and Vitest.

Can code triggered by animation-related events be unit-tested?


Solution

  • Tests usually run via jsdom which does not implement animation/transition logic. (See e.g. #1781, #3239)

    The Svelte repository is also lacking in tests around this functionality.
    E.g. PR #11208 notes:

    No test because you can't really test for this stuff in JSDOM.

    The Vitest environment can also be set to use happy-dom but that also does not look like it supports the relevant DOM logic/events.