jrh
jrh

Reputation: 4193

How can I reset the jsdom instance when using vitest in order to test a History-based router?

I'd like to do some integration testing of my svelte + page.js-based router using vitest, but I'm running into an issue where the jsdom instance only updates correctly once per test file.

In the following setup, either test will pass when run with .only or if I split each test into its own file. But when they run in sequence, the second one will always fail. Inspecting the DOM with screen.debug() reveals that it's empty, and calls to act or tick don't seem to do anything.

I suspect it has something to do with how jsdom is interacting with the History API, but I'm not sure where to go from here.

Root.svelte

<script>
  import page from 'page'

  import SignIn from './SignIn/SignIn.svelte'
  import Upload from './Upload/Upload.svelte'
  import { authenticationToken } from './Root.stores.js'

  let currentPage

  page('/', () => {
    page.redirect('/sign-in')
  })

  page('/sign-in', () => {
    currentPage = SignIn
  })

  page('/upload', () => {
    if ($authenticationToken === null) {
      return page.redirect('/sign-in')
    }

    currentPage = Upload
  })

  page.start()
</script>

<svelte:component this={ currentPage } />

Root.svelte.test.js

import page from 'page'
import Root from './Root.svelte'
import { authenticationToken } from './Root.stores.js'

it('redirects to sign in when not authenticated', async () => {
  vi.spyOn(page, 'redirect')
  authenticationToken.set(null)

  const { act } = setupComponent(Root)
  await act(() => page('/upload'))

  expect(page.redirect).toHaveBeenCalledWith('/sign-in')
})

it('displays the upload screen when authenticated', async () => {
  authenticationToken.set('token')

  const { act } = setupComponent(Root)
  await act(() => page('/upload'))

  expect(document.getElementById('upload')).toBeInTheDocument()
})

Other Research

The issue is similar to this one in the jest project. In that issue, the recommendation was to call jsdom.reconfigure() in a beforeEach block, but I don't know how to get a hold of the jsdom instance in vitest in order to try that.

Any ideas or alternative approaches welcome, thanks!

Upvotes: 8

Views: 1923

Answers (2)

balu
balu

Reputation: 3831

The accepted answer suggests using jsdom.reconfigure() but this doesn't help with resetting the JSDom instance.

Here's an approach that seems to work (inspired by this comment on Github):

import { JSDOM } from 'jsdom';
import { beforeEach } from 'vitest';

beforeEach(() => {
  const dom = new JSDOM('<html><head></head><body></body></html>', {
    url: 'https://some.url.tld' // Necessary for window.localStorage to work
  });

  // @ts-ignore
  global.window = dom.window;
  global.document = dom.window.document;
  global.navigator = dom.window.navigator;
  global.location = dom.window.location;
  global.XMLHttpRequest = dom.window.XMLHttpRequest;
})

Upvotes: 1

iradonov
iradonov

Reputation: 54

Since version 1.3.0, Vitest exposes a jsdom global variable, so it should be possible to call

jsdom.reconfigure(/* ... */)

See also https://vitest.dev/config/#environment (scroll to the bottom of the section)

Upvotes: 3

Related Questions