diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 19c1908..778d507 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -14,6 +14,7 @@ module.exports = { plugins: ['svelte', 'simple-import-sort', 'json-files'], rules: { 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', }, overrides: [ { @@ -22,6 +23,9 @@ module.exports = { parserOptions: { parser: '@typescript-eslint/parser', }, + rules: { + 'no-undef-init': 'off', + }, }, { files: ['*.ts'], diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c6e83fa..be47107 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,9 @@ on: branches: [main, next] pull_request: branches: [main, next] + schedule: + # Tuesdays at 14:45 UTC (10:45 EST) + - cron: 45 14 * * 1 concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -16,11 +19,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 diff --git a/.prettierignore b/.prettierignore index 113f29b..5e50c20 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ scripts/* .eslintignore .prettierignore +.all-contributorsrc diff --git a/.prettierrc.yaml b/.prettierrc.yaml index e1bf864..0a2ace3 100644 --- a/.prettierrc.yaml +++ b/.prettierrc.yaml @@ -4,6 +4,6 @@ trailingComma: es5 plugins: - prettier-plugin-svelte overrides: - - files: "*.svelte" + - files: '*.svelte' options: parser: svelte diff --git a/README.md b/README.md index d49bbae..0a39429 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,21 @@ This library has `peerDependencies` listings for `svelte >= 3`. You may also be interested in installing `@testing-library/jest-dom` so you can use [the custom jest matchers](https://github.com/testing-library/jest-dom). +### Svelte 5 support + +If you are riding the bleeding edge of Svelte 5, you'll need to either +import from `@testing-library/svelte/svelte5` instead of `@testing-library/svelte`, or have your `vite.config.js` contains the following alias: + +``` +export default defineConfig(({ }) => ({ + test: { + alias: { + '@testing-library/svelte': '@testing-library/svelte/svelte5' + } + }, +})) +``` + ## Docs See the [**docs**](https://testing-library.com/docs/svelte-testing-library/intro) over at the Testing Library website. diff --git a/package.json b/package.json index 20bc1c2..1948cfc 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,10 @@ "types": "./types/index.d.ts", "default": "./src/index.js" }, + "./svelte5": { + "types": "./types/index.d.ts", + "default": "./src/svelte5-index.js" + }, "./vitest": { "default": "./src/vitest.js" } @@ -64,13 +68,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", @@ -89,16 +93,16 @@ "eslint-plugin-svelte": "2.35.1", "eslint-plugin-vitest-globals": "1.4.0", "expect-type": "^0.17.3", - "happy-dom": "^13.3.1", + "happy-dom": "^14.7.1", "jsdom": "^22.1.0", "npm-run-all": "^4.1.5", "prettier": "3.2.4", "prettier-plugin-svelte": "3.1.2", - "svelte": "^3 || ^4", + "svelte": "^3 || ^4 || ^5", "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" } } diff --git a/src/__tests__/__snapshots__/render.test.js.snap b/src/__tests__/__snapshots__/render.test.js.snap deleted file mode 100644 index a31cf2e..0000000 --- a/src/__tests__/__snapshots__/render.test.js.snap +++ /dev/null @@ -1,25 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`render > should accept svelte component options 1`] = ` - -
-

- Hello - World - ! -

- -
- we have context -
- - - -
-
- -`; diff --git a/src/__tests__/act.test.js b/src/__tests__/act.test.js index 6eafc0d..75c9ded 100644 --- a/src/__tests__/act.test.js +++ b/src/__tests__/act.test.js @@ -1,25 +1,13 @@ -import { beforeEach, describe, expect, test } from 'vitest' +import { setTimeout } from 'node:timers/promises' + +import { act, render } from '@testing-library/svelte' +import { describe, expect, test } from 'vitest' -import { act, fireEvent, render as stlRender } from '..' import Comp from './fixtures/Comp.svelte' describe('act', () => { - let props - - const render = () => { - return stlRender(Comp, { - props - }) - } - - beforeEach(() => { - props = { - name: 'World' - } - }) - test('state updates are flushed', async () => { - const { getByText } = render() + const { getByText } = render(Comp) const button = getByText('Button') expect(button).toHaveTextContent('Button') @@ -31,24 +19,13 @@ describe('act', () => { expect(button).toHaveTextContent('Button Clicked') }) - test('findByTestId returns the element', async () => { - const { findByTestId } = render() - - expect(await findByTestId('test')).toHaveTextContent(`Hello ${props.name}!`) - }) - test('accepts async functions', async () => { - const sleep = (ms) => - new Promise((resolve) => { - setTimeout(() => resolve(), ms) - }) - - const { getByText } = render() + const { getByText } = render(Comp) const button = getByText('Button') await act(async () => { - await sleep(100) - await fireEvent.click(button) + await setTimeout(100) + button.click() }) expect(button).toHaveTextContent('Button Clicked') diff --git a/src/__tests__/auto-cleanup-skip.test.js b/src/__tests__/auto-cleanup-skip.test.js index 265f55b..db65447 100644 --- a/src/__tests__/auto-cleanup-skip.test.js +++ b/src/__tests__/auto-cleanup-skip.test.js @@ -7,7 +7,7 @@ describe('auto-cleanup-skip', () => { beforeAll(async () => { process.env.STL_SKIP_AUTO_CLEANUP = 'true' - const stl = await import('..') + const stl = await import('@testing-library/svelte') render = stl.render }) diff --git a/src/__tests__/auto-cleanup.test.js b/src/__tests__/auto-cleanup.test.js index 349ee39..206d101 100644 --- a/src/__tests__/auto-cleanup.test.js +++ b/src/__tests__/auto-cleanup.test.js @@ -1,6 +1,6 @@ +import { render } from '@testing-library/svelte' import { describe, expect, test } from 'vitest' -import { render } from '..' import Comp from './fixtures/Comp.svelte' describe('auto-cleanup', () => { diff --git a/src/__tests__/cleanup.test.js b/src/__tests__/cleanup.test.js new file mode 100644 index 0000000..7131624 --- /dev/null +++ b/src/__tests__/cleanup.test.js @@ -0,0 +1,35 @@ +import { cleanup, render } from '@testing-library/svelte' +import { describe, expect, test, vi } from 'vitest' + +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', () => { + 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() + }) +}) diff --git a/src/__tests__/context.test.js b/src/__tests__/context.test.js index effdef4..da54b9d 100644 --- a/src/__tests__/context.test.js +++ b/src/__tests__/context.test.js @@ -1,6 +1,6 @@ +import { render } from '@testing-library/svelte' import { expect, test } from 'vitest' -import { render } from '..' import Comp from './fixtures/Context.svelte' test('can set a context', () => { diff --git a/src/__tests__/debug.test.js b/src/__tests__/debug.test.js index 1072a0f..e2a9eda 100644 --- a/src/__tests__/debug.test.js +++ b/src/__tests__/debug.test.js @@ -1,24 +1,18 @@ import { prettyDOM } from '@testing-library/dom' -import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { render } from '@testing-library/svelte' +import { describe, expect, test, vi } from 'vitest' -import { render } from '..' import Comp from './fixtures/Comp.svelte' describe('debug', () => { - beforeEach(() => { - vi.spyOn(console, 'log').mockImplementation(() => { }) - }) - - afterEach(() => { - console.log.mockRestore() - }) + test('pretty prints the base element', () => { + vi.stubGlobal('console', { log: vi.fn(), warn: vi.fn(), error: vi.fn() }) - test('pretty prints the container', () => { - const { container, debug } = render(Comp, { props: { name: 'world' } }) + const { baseElement, debug } = render(Comp, { props: { name: 'world' } }) debug() expect(console.log).toHaveBeenCalledTimes(1) - expect(console.log).toHaveBeenCalledWith(prettyDOM(container)) + expect(console.log).toHaveBeenCalledWith(prettyDOM(baseElement)) }) }) diff --git a/src/__tests__/events.test.js b/src/__tests__/events.test.js index fccf990..e0aba4d 100644 --- a/src/__tests__/events.test.js +++ b/src/__tests__/events.test.js @@ -1,6 +1,6 @@ +import { fireEvent, render } from '@testing-library/svelte' import { describe, expect, test } from 'vitest' -import { fireEvent, render } from '..' import Comp from './fixtures/Comp.svelte' describe('events', () => { @@ -8,8 +8,9 @@ describe('events', () => { const { getByText } = render(Comp, { props: { name: 'World' } }) const button = getByText('Button') - await fireEvent.click(button) + const result = fireEvent.click(button) + await expect(result).resolves.toBe(true) expect(button).toHaveTextContent('Button Clicked') }) @@ -17,14 +18,15 @@ describe('events', () => { const { getByText } = render(Comp, { props: { name: 'World' } }) const button = getByText('Button') - await fireEvent( + const result = fireEvent( button, new MouseEvent('click', { bubbles: true, - cancelable: true + cancelable: true, }) ) + await expect(result).resolves.toBe(true) expect(button).toHaveTextContent('Button Clicked') }) }) diff --git a/src/__tests__/fixtures/Comp.svelte b/src/__tests__/fixtures/Comp.svelte index ec04c05..ba23d88 100644 --- a/src/__tests__/fixtures/Comp.svelte +++ b/src/__tests__/fixtures/Comp.svelte @@ -1,23 +1,17 @@

Hello {name}!

-
we have {contextName}
- diff --git a/src/__tests__/fixtures/Comp2.svelte b/src/__tests__/fixtures/Comp2.svelte deleted file mode 100644 index 104e81a..0000000 --- a/src/__tests__/fixtures/Comp2.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - - -

Hello {name}!

- - diff --git a/src/__tests__/fixtures/Context.svelte b/src/__tests__/fixtures/Context.svelte index be8253b..d6515d5 100644 --- a/src/__tests__/fixtures/Context.svelte +++ b/src/__tests__/fixtures/Context.svelte @@ -1,7 +1,7 @@
{ctx.message}
diff --git a/src/__tests__/fixtures/Mounter.svelte b/src/__tests__/fixtures/Mounter.svelte index 477bb34..51ebcd8 100644 --- a/src/__tests__/fixtures/Mounter.svelte +++ b/src/__tests__/fixtures/Mounter.svelte @@ -1,11 +1,19 @@