Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Svelte v5-next support #321

Merged
merged 5 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,23 @@ jobs:
if: ${{ !contains(github.head_ref, 'all-contributors') }}
name: Node ${{ matrix.node }}, Svelte ${{ matrix.svelte }}, ${{ matrix.test-runner }}
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
matrix:
node: ['16', '18', '20']
svelte: ['3', '4']
test-runner: ['vitest:jsdom', 'vitest:happy-dom']
experimental: [false]
include:
- node: '20'
svelte: 'next'
test-runner: 'vitest:jsdom'
experimental: true
- node: '20'
svelte: 'next'
test-runner: 'vitest:happy-dom'
experimental: true

steps:
- name: ⬇️ Checkout repo
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@
"contributors:generate": "all-contributors generate"
},
"peerDependencies": {
"svelte": "^3 || ^4"
"svelte": "^3 || ^4 || ^5"
},
"dependencies": {
"@testing-library/dom": "^9.3.1"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.4.2",
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@testing-library/jest-dom": "^6.3.0",
"@testing-library/user-event": "^14.5.2",
"@typescript-eslint/eslint-plugin": "6.19.1",
Expand All @@ -94,11 +94,11 @@
"npm-run-all": "^4.1.5",
"prettier": "3.2.4",
"prettier-plugin-svelte": "3.1.2",
"svelte": "^3 || ^4",
"svelte": "^4.2.10",
"svelte-check": "^3.6.3",
"svelte-jester": "^3.0.0",
"typescript": "^5.3.3",
"vite": "^4.3.9",
"vite": "^5.1.1",
"vitest": "^0.33.0"
}
}
27 changes: 25 additions & 2 deletions src/__tests__/__snapshots__/render.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`render > should accept svelte component options 1`] = `
exports[`render > should accept svelte v4 component options 1`] = `
<body>
<div>
<h1
Expand All @@ -18,8 +18,31 @@ exports[`render > should accept svelte component options 1`] = `
<button>
Button
</button>
<!--&lt;Comp&gt;-->
<div />
</div>
</body>
`;

exports[`render > should accept svelte v5 component options 1`] = `
<body>



<section>
<h1
data-testid="test"
>
Hello World!
</h1>

<div>
we have context
</div>

<button>
Button
</button>

</section>
</body>
`;
35 changes: 35 additions & 0 deletions src/__tests__/cleanup.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, test, vi } from 'vitest'

import { act, cleanup, render } from '..'
import Mounter from './fixtures/Mounter.svelte'

const onExecuted = vi.fn()
const onDestroyed = vi.fn()
const renderSubject = () => render(Mounter, { onExecuted, onDestroyed })

describe('cleanup', () => {
test('cleanup deletes element', async () => {
renderSubject()
cleanup()

expect(document.body).toBeEmptyDOMElement()
})

test('cleanup unmounts component', async () => {
await act(renderSubject)
cleanup()

expect(onDestroyed).toHaveBeenCalledOnce()
})

test('cleanup handles unexpected errors during mount', () => {
onExecuted.mockImplementation(() => {
throw new Error('oh no!')
})

expect(renderSubject).toThrowError()
cleanup()

expect(document.body).toBeEmptyDOMElement()
})
})
18 changes: 13 additions & 5 deletions src/__tests__/fixtures/Mounter.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
<script>
import { onDestroy,onMount } from 'svelte'
import { onDestroy, onMount } from 'svelte'

export let onMounted
export let onDestroyed
export let onExecuted = undefined
export let onMounted = undefined
export let onDestroyed = undefined

onMount(() => onMounted())
onDestroy(() => onDestroyed())
onExecuted?.()

onMount(() => {
onMounted?.()
})

onDestroy(() => {
onDestroyed?.()
})
</script>

<button />
20 changes: 11 additions & 9 deletions src/__tests__/fixtures/Rerender.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<script>
import { getContext, onMount } from 'svelte'
import { onDestroy, onMount } from 'svelte'

export let name
export let onExecuted = undefined
export let onMounted = undefined
export let onDestroyed = undefined

const mountCounter = getContext('mountCounter')
export let name = ''

onMount(() => {
mountCounter.update((i) => i + 1)
})
</script>
onExecuted?.()

onMount(() => onMounted?.())

<h1 data-testid="test">Hello {name}!</h1>
onDestroy(() => onDestroyed?.())
</script>

<div data-testid="mount-counter">{$mountCounter}</div>
<div data-testid="test">Hello {name}!</div>
26 changes: 13 additions & 13 deletions src/__tests__/mount.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,31 @@ 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()
const onMounted = vi.fn()
const onDestroyed = vi.fn()
const renderSubject = () => render(Mounter, { onMounted, onDestroyed })

describe('mount and destroy', () => {
test('component is mounted', async () => {
await act(() => {
render(Mounter, { onMounted: handleMount, onDestroyed: handleDestroy })
})
renderSubject()

const content = screen.getByRole('button')

expect(handleMount).toHaveBeenCalledOnce()
expect(content).toBeInTheDocument()
await act()
expect(onMounted).toHaveBeenCalledOnce()
})

test('component is destroyed', async () => {
const { unmount } = render(Mounter, {
onMounted: handleMount,
onDestroyed: handleDestroy,
})
const { unmount } = renderSubject()

await act()
unmount()

await act(() => unmount())
const content = screen.queryByRole('button')

expect(handleDestroy).toHaveBeenCalledOnce()
expect(content).not.toBeInTheDocument()
await act()
expect(onDestroyed).toHaveBeenCalledOnce()
})
})
61 changes: 40 additions & 21 deletions src/__tests__/render.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { VERSION as SVELTE_VERSION } from 'svelte/compiler'
import { beforeEach, describe, expect, test } from 'vitest'

import { act, render as stlRender } from '..'
Expand All @@ -11,13 +12,13 @@ describe('render', () => {
return stlRender(Comp, {
target: document.body,
props,
...additional
...additional,
})
}

beforeEach(() => {
props = {
name: 'World'
name: 'World',
}
})

Expand All @@ -41,7 +42,9 @@ describe('render', () => {
})

test('change props with accessors', async () => {
const { component, getByText } = render({ accessors: true })
const { component, getByText } = render(
SVELTE_VERSION < '5' ? { accessors: true } : {}
)

expect(getByText('Hello World!')).toBeInTheDocument()

Expand All @@ -59,23 +62,41 @@ describe('render', () => {
expect(getByText('Hello World!')).toBeInTheDocument()
})

test('should accept svelte component options', () => {
const target = document.createElement('div')
const div = document.createElement('div')
document.body.appendChild(target)
target.appendChild(div)
const { container } = stlRender(Comp, {
target,
anchor: div,
props: { name: 'World' },
context: new Map([['name', 'context']])
})
expect(container).toMatchSnapshot()
})
test.runIf(SVELTE_VERSION < '5')(
'should accept svelte v4 component options',
() => {
const target = document.createElement('div')
const div = document.createElement('div')
document.body.appendChild(target)
target.appendChild(div)
const { container } = stlRender(Comp, {
target,
anchor: div,
props: { name: 'World' },
context: new Map([['name', 'context']]),
})
expect(container).toMatchSnapshot()
}
)

test.runIf(SVELTE_VERSION >= '5')(
'should accept svelte v5 component options',
() => {
const target = document.createElement('section')
document.body.appendChild(target)

const { container } = stlRender(Comp, {
target,
props: { name: 'World' },
context: new Map([['name', 'context']]),
})
expect(container).toMatchSnapshot()
}
)

test('should throw error when mixing svelte component options and props', () => {
expect(() => {
stlRender(Comp, { anchor: '', name: 'World' })
stlRender(Comp, { props: {}, name: 'World' })
}).toThrow(/Unknown options were found/)
})

Expand All @@ -93,10 +114,8 @@ describe('render', () => {

test("accept the 'context' option", () => {
const { getByText } = stlRender(Comp, {
props: {
name: 'Universe'
},
context: new Map([['name', 'context']])
props: { name: 'Universe' },
context: new Map([['name', 'context']]),
})

expect(getByText('we have context')).toBeInTheDocument()
Expand Down
19 changes: 11 additions & 8 deletions src/__tests__/rerender.test.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
/**
* @jest-environment jsdom
*/
import { expect, test, vi } from 'vitest'
import { describe, expect, test, vi } from 'vitest'
import { writable } from 'svelte/store'

import { render, waitFor } from '..'
import { act, render, waitFor } from '..'
import Comp from './fixtures/Rerender.svelte'

const mountCounter = writable(0)

test('mounts new component successfully', async () => {
const onMounted = vi.fn()
const onDestroyed = vi.fn()

const { getByTestId, rerender } = render(Comp, {
props: { name: 'World 1' },
context: new Map(Object.entries({ mountCounter })),
props: { name: 'World 1', onMounted, onDestroyed },
})

const expectToRender = (content) =>
waitFor(() => {
expect(getByTestId('test')).toHaveTextContent(content)
expect(getByTestId('mount-counter')).toHaveTextContent('1')
expect(onMounted).toHaveBeenCalledOnce()
})

await expectToRender('Hello World 1!')
Expand All @@ -27,12 +27,15 @@ test('mounts new component successfully', async () => {

rerender({ props: { name: 'World 2' } })
await expectToRender('Hello World 2!')
expect(onDestroyed).not.toHaveBeenCalled()

expect(console.warn).toHaveBeenCalled()
expect(console.warn).toHaveBeenCalledOnce()

console.warn.mockClear()
onDestroyed.mockReset()
rerender({ name: 'World 3' })
await expectToRender('Hello World 3!')
expect(onDestroyed).not.toHaveBeenCalled()

expect(console.warn).not.toHaveBeenCalled()
})
3 changes: 2 additions & 1 deletion src/__tests__/transition.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { userEvent } from '@testing-library/user-event'
import { VERSION as SVELTE_VERSION } from 'svelte/compiler'
import { beforeEach, describe, expect, test, vi } from 'vitest'

import { render, screen, waitFor } from '..'
import Transitioner from './fixtures/Transitioner.svelte'

describe('transitions', () => {
describe.runIf(SVELTE_VERSION < '5')('transitions', () => {
beforeEach(() => {
if (window.navigator.userAgent.includes('jsdom')) {
const raf = (fn) => setTimeout(() => fn(new Date()), 16)
Expand Down
Loading
Loading