wd-David / svelte-component-test-recipes

Svelte component test recipes using Vitest & Testing Library with TypeScript
155 stars 5 forks source link

Overwriting the url of $app/stores in single test #50

Open jenseo opened 1 year ago

jenseo commented 1 year ago

Hey, I'm quite new to svelte/sveltekit so I might be missing something, but I'm trying to test a component making use of some logic of $page.url.pathname from the $app/stores module.

This requires me to be able to define the url pathname, but in a single test only, meaning I would have to override parts of the mock from the setupTests.ts, namely the vi.mock('$app/stores'... part.

Do you have an idea on how to go about doing that, so far all my efforts have been unsuccessful and haven't found anyone with a solution online either. But you seem to be the guru of it, so I thought I'd ask directly :D

Thanks for a really great resource!

ax2mx commented 10 months ago

Hi, @davipon !

Thanks for your helpful recipes!

I have the same question actually. I've stuck on the issue trying to test some svelte component's logic depending on current page i.e. the value of $page.url.pathname.

Looking forward to your advice. Thanks!

uioporqwerty commented 1 month ago

For those that need it in the future, here is one approach:

Turns out you can use the setup in that recipes repo and then just use vi.mocked as this describes: https://github.com/vitest-dev/vitest/discussions/4328

vitest-setup.ts

export const createMockPage = (
    page: Partial<Page<Record<string, string>, string | null>> = {}
): Page<Record<string, string>, string | null> => {
    const defaultPage = {
        url: new URL('http://localhost'),
        params: {},
        route: {
            id: null
        },
        status: 200,
        error: null,
        data: {},
        form: undefined,
        state: {}
    };

    return {
        ...defaultPage,
        ...page
    };
};

vi.mock('$app/stores', (): typeof stores => {
    const getStores: typeof stores.getStores = () => {
        const navigating = readable<Navigation | null>(null);
        const page = readable<Page>(createMockPage());
        const updated = { subscribe: readable(false).subscribe, check: async () => false };

        return { navigating, page, updated };
    };

    const page: typeof stores.page = {
        subscribe(fn) {
            return getStores().page.subscribe(fn);
        }
    };
    const navigating: typeof stores.navigating = {
        subscribe(fn) {
            return getStores().navigating.subscribe(fn);
        }
    };
    const updated: typeof stores.updated = {
        subscribe(fn) {
            return getStores().updated.subscribe(fn);
        },
        check: async () => false
    };

    return {
        getStores,
        navigating,
        page,
        updated
    };
});

And then in an actual test:

                const mockPage = createMockPage({
            url: new URL('http://localhost/onboarding')
        });

        vi.mocked(stores.page).subscribe = vi.fn((fn) => {
            fn(mockPage);
            return readable(mockPage).subscribe(fn);
        });

Of course that createMockPage can be made more typesafe too but this accomplishes what I need.