Repository: testing-library/svelte-testing-library
Pull request #303 (2024-01-29 23:45:51)

test: add onMount test and replace onDestory test
## Overview

This PR adds an `onMount` test and simplifies the `onDestroy` test. Supersedes #254 

## Change log

- Add `Mounter.svelte` fixture with function prop hookups to `onMount` and `onDestroy`, letting us test via spies rather than the misdirection of rendering
- Removed `Stopwatch.svelte` since it was a little confusing and didn't directly help the tests (see above point about misdirection)
- Removed old unmount tests because they:
    - Exercised more code than was necessary to test mount/unmount
    - Inappropriately modified the global `console` object without cleanup
    - Seemed to be testing implementation details (`console.warn`) that no longer exist


Patch:
diff --git a/vite.config.js b/vite.config.js
index f261029..4baf76f 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -2,14 +2,23 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'
 import { defineConfig } from 'vite'
 
 // https://vitejs.dev/config/
-export default defineConfig({
+export default defineConfig(({ mode }) => ({
   plugins: [svelte()],
+  resolve: {
+    // Ensure `browser` exports are used in tests
+    // Vitest prefers modules' `node` export by default
+    // Svelte's `node` export is its SSR bundle, which does not have onMount
+    // https://github.com/testing-library/svelte-testing-library/issues/222#issuecomment-1909993331
+    conditions: mode === 'test' ? ['browser'] : [],
+  },
   test: {
     environment: 'jsdom',
     setupFiles: ['./src/__tests__/_vitest-setup.js'],
+    mockReset: true,
+    unstubGlobals: true,
     coverage: {
       provider: 'v8',
       include: ['src'],
     },
   },
-})
+}))


Test patch:
diff --git a/src/__tests__/fixtures/Mounter.svelte b/src/__tests__/fixtures/Mounter.svelte
new file mode 100644
index 0000000..477bb34
--- /dev/null
+++ b/src/__tests__/fixtures/Mounter.svelte
@@ -0,0 +1,11 @@
+<script>
+  import { onDestroy,onMount } from 'svelte'
+
+  export let onMounted
+  export let onDestroyed
+
+  onMount(() => onMounted())
+  onDestroy(() => onDestroyed())
+</script>
+
+<button />
diff --git a/src/__tests__/fixtures/Stopwatch.svelte b/src/__tests__/fixtures/Stopwatch.svelte
deleted file mode 100644
index 2c275e5..0000000
--- a/src/__tests__/fixtures/Stopwatch.svelte
+++ /dev/null
@@ -1,40 +0,0 @@
-<script>
-  import { onDestroy } from 'svelte'
-
-  let timer
-  let lapse = 0
-  let running = false
-
-  function handleRunClick () {
-    if (running) {
-      clearInterval(timer)
-    } else {
-      const startTime = Date.now() - lapse
-
-      timer = setInterval(() => {
-        lapse = Date.now() - startTime
-      }, 1)
-    }
-    running = true
-  }
-
-  function handleClearClick () {
-    clearInterval(timer)
-    lapse = 0
-    running = false
-  }
-
-  onDestroy(() => {
-    clearInterval(timer)
-  })
-</script>
-
-<style></style>
-
-<span>{lapse}ms</span>
-
-<button on:click={handleRunClick}>
-  {running ? 'Stop' : 'Start'}
-</button>
-
-<button on:click={handleClearClick}>Clear</button>
diff --git a/src/__tests__/mount.test.js b/src/__tests__/mount.test.js
new file mode 100644
index 0000000..830b513
--- /dev/null
+++ b/src/__tests__/mount.test.js
@@ -0,0 +1,33 @@
+import { describe, expect, test, vi } from 'vitest'
+
+import { act, render, screen } from '..'
+import Mounter from './fixtures/Mounter.svelte'
+
+describe('mount and destroy', () => {
+  const handleMount = vi.fn()
+  const handleDestroy = vi.fn()
+
+  test('component is mounted', async () => {
+    await act(() => {
+      render(Mounter, { onMounted: handleMount, onDestroyed: handleDestroy })
+    })
+
+    const content = screen.getByRole('button')
+
+    expect(handleMount).toHaveBeenCalledOnce()
+    expect(content).toBeInTheDocument()
+  })
+
+  test('component is destroyed', async () => {
+    const { unmount } = render(Mounter, {
+      onMounted: handleMount,
+      onDestroyed: handleDestroy,
+    })
+
+    await act(() => unmount())
+    const content = screen.queryByRole('button')
+
+    expect(handleDestroy).toHaveBeenCalledOnce()
+    expect(content).not.toBeInTheDocument()
+  })
+})
diff --git a/src/__tests__/unmount.test.js b/src/__tests__/unmount.test.js
deleted file mode 100644
index 86a29dd..0000000
--- a/src/__tests__/unmount.test.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import { describe, expect, test, vi } from 'vitest'
-
-import { act, fireEvent, render } from '..'
-import Stopwatch from './fixtures/Stopwatch.svelte'
-
-describe('unmount', () => {
-  test('unmounts component successfully', async () => {
-    console.warn = vi.fn()
-
-    const { unmount, getByText, container } = render(Stopwatch)
-
-    await fireEvent.click(getByText('Start'))
-
-    unmount()
-
-    // Hey there reader! You don't need to have an assertion like this one
-    // this is just me making sure that the unmount function works.
-    // You don't need to do this in your apps. Just rely on the fact that this works.
-    expect(container.innerHTML).toBe('<div></div>')
-
-    await act()
-    expect(console.warn).not.toHaveBeenCalled()
-  })
-
-  test('destroying component directly and calling unmount does not log warning', async () => {
-    console.warn = vi.fn()
-
-    const { unmount, component } = render(Stopwatch)
-
-    component.$destroy()
-    unmount()
-
-    expect(console.warn).not.toHaveBeenCalled()
-  })
-})