From 334a7594d2685b4a10ae1b2e67d3b0d5e2198f91 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Thu, 25 Apr 2024 11:29:45 -0400 Subject: [PATCH 01/37] ci(dependabot): group dependency updates and reduce frequency (#364) --- .github/dependabot.yml | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8b4845b..2e64a34 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,8 +1,33 @@ version: 2 updates: - - package-ecosystem: npm + # Update npm dependencies + - package-ecosystem: 'npm' directory: '/' schedule: - interval: daily - time: '10:00' - open-pull-requests-limit: 10 + interval: 'monthly' + groups: + lint: + patterns: + - '*eslint*' + - '*prettier*' + - '*typescript*' + test: + patterns: + - '*svelte*' + - '*testing-library*' + - '*vite*' + - '*vitest*' + - 'jsdom' + - 'happy-dom' + development: + dependency-type: 'development' + + # Update GitHub Actions dependencies + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'monthly' + groups: + actions: + patterns: + - '*' From 5464ac24d147dadf7be80788ca8dcfe825c0173b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 23:55:49 -0400 Subject: [PATCH 02/37] chore(deps): bump the test group with 4 updates (#368) Updates `@testing-library/dom` from 9.3.4 to 10.0.0 - [Release notes](https://github.com/testing-library/dom-testing-library/releases) - [Changelog](https://github.com/testing-library/dom-testing-library/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/dom-testing-library/compare/v9.3.4...v10.0.0) Updates `@vitest/coverage-v8` from 0.33.0 to 1.5.2 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v1.5.2/packages/coverage-v8) Updates `jsdom` from 22.1.0 to 24.0.0 - [Release notes](https://github.com/jsdom/jsdom/releases) - [Changelog](https://github.com/jsdom/jsdom/blob/main/Changelog.md) - [Commits](https://github.com/jsdom/jsdom/compare/22.1.0...24.0.0) Updates `vitest` from 0.33.0 to 1.5.2 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v1.5.2/packages/vitest) Updates `expect-type` from 0.17.3 to 0.19.0. - [Release notes](https://github.com/mmkal/expect-type/releases) - [Commits](https://github.com/mmkal/expect-type/compare/v0.17.3...0.19.0) Signed-off-by: dependabot[bot] Co-authored-by: Michael Cousins --- .github/dependabot.yml | 5 +++-- package.json | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2e64a34..434a8bb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,8 +17,9 @@ updates: - '*testing-library*' - '*vite*' - '*vitest*' - - 'jsdom' - - 'happy-dom' + - '*jsdom*' + - '*happy-dom*' + - 'expect-type' development: dependency-type: 'development' diff --git a/package.json b/package.json index 307e6ba..5e00bbe 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ } }, "dependencies": { - "@testing-library/dom": "^9.3.1" + "@testing-library/dom": "^10.0.0" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.2", @@ -95,7 +95,7 @@ "@testing-library/user-event": "^14.5.2", "@typescript-eslint/eslint-plugin": "6.19.1", "@typescript-eslint/parser": "6.19.1", - "@vitest/coverage-v8": "^0.33.0", + "@vitest/coverage-v8": "^1.5.2", "all-contributors-cli": "^6.26.1", "doctoc": "^2.2.1", "eslint": "8.56.0", @@ -108,9 +108,9 @@ "eslint-plugin-simple-import-sort": "10.0.0", "eslint-plugin-svelte": "2.35.1", "eslint-plugin-vitest-globals": "1.4.0", - "expect-type": "^0.17.3", + "expect-type": "^0.19.0", "happy-dom": "^14.7.1", - "jsdom": "^22.1.0", + "jsdom": "^24.0.0", "npm-run-all": "^4.1.5", "prettier": "3.2.4", "prettier-plugin-svelte": "3.1.2", @@ -119,6 +119,6 @@ "svelte-jester": "^3.0.0", "typescript": "^5.3.3", "vite": "^5.1.1", - "vitest": "^0.33.0" + "vitest": "^1.5.2" } } From 266e2dfd1ea0f1214186f49e604adf43734c0a55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 23:47:00 -0400 Subject: [PATCH 03/37] chore(deps-dev): bump the lint group across 1 directory with 9 updates (#370) Bumps the lint group with 9 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `6.19.1` | `7.8.0` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `6.19.1` | `7.8.0` | | [eslint](https://github.com/eslint/eslint) | `8.56.0` | `8.57.0` | | [eslint-plugin-simple-import-sort](https://github.com/lydell/eslint-plugin-simple-import-sort) | `10.0.0` | `12.1.0` | | [eslint-plugin-svelte](https://github.com/sveltejs/eslint-plugin-svelte) | `2.35.1` | `2.38.0` | | [eslint-plugin-vitest-globals](https://github.com/saqqdy/eslint-plugin-vitest-globals) | `1.4.0` | `1.5.0` | | [prettier](https://github.com/prettier/prettier) | `3.2.4` | `3.2.5` | | [prettier-plugin-svelte](https://github.com/sveltejs/prettier-plugin-svelte) | `3.1.2` | `3.2.3` | Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Cousins --- .github/dependabot.yml | 7 +++++++ package.json | 16 ++++++++-------- tsconfig.json | 4 ++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 434a8bb..d4a16b5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -23,6 +23,13 @@ updates: development: dependency-type: 'development' + # TODO(mcous, 2024-04-30): update to ESLint v9 + flat config + ignore: + - dependency-name: 'eslint' + versions: ['>=9'] + - dependency-name: 'eslint-plugin-n' + versions: ['>=17'] + # Update GitHub Actions dependencies - package-ecosystem: 'github-actions' directory: '/' diff --git a/package.json b/package.json index 5e00bbe..b7182e5 100644 --- a/package.json +++ b/package.json @@ -93,27 +93,27 @@ "@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", - "@typescript-eslint/parser": "6.19.1", + "@typescript-eslint/eslint-plugin": "7.8.0", + "@typescript-eslint/parser": "7.8.0", "@vitest/coverage-v8": "^1.5.2", "all-contributors-cli": "^6.26.1", "doctoc": "^2.2.1", - "eslint": "8.56.0", + "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-config-standard": "17.1.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-json-files": "^4.1.0", "eslint-plugin-n": "16.6.2", "eslint-plugin-promise": "6.1.1", - "eslint-plugin-simple-import-sort": "10.0.0", - "eslint-plugin-svelte": "2.35.1", - "eslint-plugin-vitest-globals": "1.4.0", + "eslint-plugin-simple-import-sort": "12.1.0", + "eslint-plugin-svelte": "2.38.0", + "eslint-plugin-vitest-globals": "1.5.0", "expect-type": "^0.19.0", "happy-dom": "^14.7.1", "jsdom": "^24.0.0", "npm-run-all": "^4.1.5", - "prettier": "3.2.4", - "prettier-plugin-svelte": "3.1.2", + "prettier": "3.2.5", + "prettier-plugin-svelte": "3.2.3", "svelte": "^3 || ^4 || ^5", "svelte-check": "^3.6.3", "svelte-jester": "^3.0.0", diff --git a/tsconfig.json b/tsconfig.json index 504b256..2b353f3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "noEmit": true, "skipLibCheck": true, "strict": true, - "types": ["svelte", "vite/client", "vitest", "vitest/globals"], + "types": ["svelte", "vite/client", "vitest", "vitest/globals"] }, - "include": ["src", "types"], + "include": ["src", "types"] } From ac3248d0e92188f747936cc5e975c76b52a3b199 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Wed, 15 May 2024 12:09:21 -0400 Subject: [PATCH 04/37] test: fix auto-cleanup tests (#371) --- package.json | 1 - .../auto-cleanup-skip.test.js.snap | 3 -- src/__tests__/auto-cleanup-skip.test.js | 23 --------- src/__tests__/auto-cleanup.test.js | 49 ++++++++++++------- src/index.js | 3 +- 5 files changed, 31 insertions(+), 48 deletions(-) delete mode 100644 src/__tests__/__snapshots__/auto-cleanup-skip.test.js.snap delete mode 100644 src/__tests__/auto-cleanup-skip.test.js diff --git a/package.json b/package.json index b7182e5..795e117 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,6 @@ "setup": "npm install && npm run validate", "test": "vitest run --coverage", "test:watch": "vitest", - "test:update": "vitest run --update", "test:vitest:jsdom": "vitest run --coverage --environment jsdom", "test:vitest:happy-dom": "vitest run --coverage --environment happy-dom", "types": "svelte-check", diff --git a/src/__tests__/__snapshots__/auto-cleanup-skip.test.js.snap b/src/__tests__/__snapshots__/auto-cleanup-skip.test.js.snap deleted file mode 100644 index 2631c86..0000000 --- a/src/__tests__/__snapshots__/auto-cleanup-skip.test.js.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`auto-cleanup-skip > second 1`] = `""`; diff --git a/src/__tests__/auto-cleanup-skip.test.js b/src/__tests__/auto-cleanup-skip.test.js deleted file mode 100644 index db65447..0000000 --- a/src/__tests__/auto-cleanup-skip.test.js +++ /dev/null @@ -1,23 +0,0 @@ -import { beforeAll, describe, expect, test } from 'vitest' - -import Comp from './fixtures/Comp.svelte' - -describe('auto-cleanup-skip', () => { - let render - - beforeAll(async () => { - process.env.STL_SKIP_AUTO_CLEANUP = 'true' - const stl = await import('@testing-library/svelte') - render = stl.render - }) - - // This one verifies that if STL_SKIP_AUTO_CLEANUP is set - // then we DON'T auto-wire up the afterEach for folks - test('first', () => { - render(Comp, { props: { name: 'world' } }) - }) - - test('second', () => { - expect(document.body.innerHTML).toMatchSnapshot() - }) -}) diff --git a/src/__tests__/auto-cleanup.test.js b/src/__tests__/auto-cleanup.test.js index 206d101..b06d120 100644 --- a/src/__tests__/auto-cleanup.test.js +++ b/src/__tests__/auto-cleanup.test.js @@ -1,31 +1,42 @@ -import { render } from '@testing-library/svelte' -import { describe, expect, test } from 'vitest' +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' -import Comp from './fixtures/Comp.svelte' +import { IS_SVELTE_5 } from './utils.js' + +const importSvelteTestingLibrary = async () => + IS_SVELTE_5 ? import('../svelte5-index.js') : import('../index.js') + +const globalAfterEach = vi.fn() describe('auto-cleanup', () => { - // This just verifies that by importing STL in an - // environment which supports afterEach (like jest) - // we'll get automatic cleanup between tests. - test('first', () => { - render(Comp, { props: { name: 'world' } }) + beforeEach(() => { + vi.resetModules() + globalThis.afterEach = globalAfterEach }) - test('second', () => { - expect(document.body.innerHTML).toEqual('') + afterEach(() => { + delete process.env.STL_SKIP_AUTO_CLEANUP + delete globalThis.afterEach }) -}) -describe('cleanup of two components', () => { - // This just verifies that by importing STL in an - // environment which supports afterEach (like jest) - // we'll get automatic cleanup between tests. - test('first', () => { + test('calls afterEach with cleanup if globally defined', async () => { + const { render } = await importSvelteTestingLibrary() + + expect(globalAfterEach).toHaveBeenCalledTimes(1) + expect(globalAfterEach).toHaveBeenLastCalledWith(expect.any(Function)) + const globalCleanup = globalAfterEach.mock.lastCall[0] + + const { default: Comp } = await import('./fixtures/Comp.svelte') render(Comp, { props: { name: 'world' } }) - render(Comp, { props: { name: 'universe' } }) + await globalCleanup() + + expect(document.body).toBeEmptyDOMElement() }) - test('second', () => { - expect(document.body.innerHTML).toEqual('') + test('does not call afterEach if process STL_SKIP_AUTO_CLEANUP is set', async () => { + process.env.STL_SKIP_AUTO_CLEANUP = 'true' + + await importSvelteTestingLibrary() + + expect(globalAfterEach).toHaveBeenCalledTimes(0) }) }) diff --git a/src/index.js b/src/index.js index 46fd662..2e3d772 100644 --- a/src/index.js +++ b/src/index.js @@ -4,8 +4,7 @@ import { act, cleanup } from './pure.js' // If we're running in a test runner that supports afterEach // then we'll automatically run cleanup afterEach test // this ensures that tests run in isolation from each other -// if you don't like this then either import the `pure` module -// or set the STL_SKIP_AUTO_CLEANUP env variable to 'true'. +// if you don't like this then set the STL_SKIP_AUTO_CLEANUP env variable. if (typeof afterEach === 'function' && !process.env.STL_SKIP_AUTO_CLEANUP) { afterEach(async () => { await act() From cb6633349ff8c9e0958689f3c49a8aacfc092bb6 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Wed, 15 May 2024 15:24:27 -0400 Subject: [PATCH 05/37] test(jest): add Jest to CI matrix (#372) --- .github/workflows/release.yml | 8 ++++++-- jest.config.js | 23 +++++++++++++++++++++++ package.json | 4 ++++ src/__tests__/_jest-setup.js | 9 +++++++++ src/__tests__/_jest-vitest-alias.js | 25 +++++++++++++++++++++++++ src/__tests__/cleanup.test.js | 2 +- src/__tests__/mount.test.js | 4 ++-- src/__tests__/rerender.test.js | 2 +- 8 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 jest.config.js create mode 100644 src/__tests__/_jest-setup.js create mode 100644 src/__tests__/_jest-vitest-alias.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index be47107..27af7a8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: matrix: node: ['16', '18', '20'] svelte: ['3', '4'] - test-runner: ['vitest:jsdom', 'vitest:happy-dom'] + test-runner: ['vitest:jsdom', 'vitest:happy-dom', 'jest'] experimental: [false] include: - node: '20' @@ -36,6 +36,10 @@ jobs: svelte: 'next' test-runner: 'vitest:happy-dom' experimental: true + - node: '20' + svelte: 'next' + test-runner: 'jest' + experimental: true steps: - name: ⬇️ Checkout repo @@ -55,7 +59,7 @@ jobs: run: npm run test:${{ matrix.test-runner }} - name: ▶️ Run type-checks - if: ${{ matrix.node == '20' && matrix.svelte == '4' }} + if: ${{ matrix.node == '20' && matrix.svelte == '4' && matrix.test-runner == 'vitest:jsdom' }} run: npm run types - name: ⬆️ Upload coverage report diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..d6b1fde --- /dev/null +++ b/jest.config.js @@ -0,0 +1,23 @@ +import { VERSION as SVELTE_VERSION } from 'svelte/compiler' + +const IS_SVELTE_5 = SVELTE_VERSION >= '5' + +export default { + testMatch: ['/src/__tests__/**/*.test.js'], + transform: { + '^.+\\.svelte$': 'svelte-jester', + }, + moduleFileExtensions: ['js', 'svelte'], + extensionsToTreatAsEsm: ['.svelte'], + testEnvironment: 'jsdom', + setupFilesAfterEnv: ['/src/__tests__/_jest-setup.js'], + injectGlobals: false, + moduleNameMapper: { + '^vitest$': '/src/__tests__/_jest-vitest-alias.js', + '^@testing-library/svelte$': IS_SVELTE_5 + ? '/src/svelte5-index.js' + : '/src/index.js', + }, + resetMocks: true, + restoreMocks: true, +} diff --git a/package.json b/package.json index 795e117..b67db89 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "test:watch": "vitest", "test:vitest:jsdom": "vitest run --coverage --environment jsdom", "test:vitest:happy-dom": "vitest run --coverage --environment happy-dom", + "test:jest": "npx --node-options=\"--experimental-vm-modules --no-warnings\" jest --coverage", "types": "svelte-check", "validate": "npm-run-all test:vitest:* types", "contributors:add": "all-contributors add", @@ -89,6 +90,7 @@ "@testing-library/dom": "^10.0.0" }, "devDependencies": { + "@jest/globals": "^29.7.0", "@sveltejs/vite-plugin-svelte": "^3.0.2", "@testing-library/jest-dom": "^6.3.0", "@testing-library/user-event": "^14.5.2", @@ -109,6 +111,8 @@ "eslint-plugin-vitest-globals": "1.5.0", "expect-type": "^0.19.0", "happy-dom": "^14.7.1", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "jsdom": "^24.0.0", "npm-run-all": "^4.1.5", "prettier": "3.2.5", diff --git a/src/__tests__/_jest-setup.js b/src/__tests__/_jest-setup.js new file mode 100644 index 0000000..d1c255c --- /dev/null +++ b/src/__tests__/_jest-setup.js @@ -0,0 +1,9 @@ +import '@testing-library/jest-dom/jest-globals' + +import { afterEach } from '@jest/globals' +import { act, cleanup } from '@testing-library/svelte' + +afterEach(async () => { + await act() + cleanup() +}) diff --git a/src/__tests__/_jest-vitest-alias.js b/src/__tests__/_jest-vitest-alias.js new file mode 100644 index 0000000..6628c80 --- /dev/null +++ b/src/__tests__/_jest-vitest-alias.js @@ -0,0 +1,25 @@ +import { describe, jest, test } from '@jest/globals' + +export { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + test, + jest as vi, +} from '@jest/globals' + +// Add support for describe.skipIf and test.skipIf +describe.skipIf = (condition) => (condition ? describe.skip : describe) +test.skipIf = (condition) => (condition ? test.skip : test) + +// Add support for `stubGlobal` +jest.stubGlobal = (property, stub) => { + if (typeof stub === 'function') { + jest.spyOn(globalThis, property).mockImplementation(stub) + } else { + jest.replaceProperty(globalThis, property, stub) + } +} diff --git a/src/__tests__/cleanup.test.js b/src/__tests__/cleanup.test.js index 7131624..d0ae026 100644 --- a/src/__tests__/cleanup.test.js +++ b/src/__tests__/cleanup.test.js @@ -19,7 +19,7 @@ describe('cleanup', () => { renderSubject() cleanup() - expect(onDestroyed).toHaveBeenCalledOnce() + expect(onDestroyed).toHaveBeenCalledTimes(1) }) test('cleanup handles unexpected errors during mount', () => { diff --git a/src/__tests__/mount.test.js b/src/__tests__/mount.test.js index 48d985f..e25c429 100644 --- a/src/__tests__/mount.test.js +++ b/src/__tests__/mount.test.js @@ -15,7 +15,7 @@ describe('mount and destroy', () => { expect(content).toBeInTheDocument() await act() - expect(onMounted).toHaveBeenCalledOnce() + expect(onMounted).toHaveBeenCalledTimes(1) }) test('component is destroyed', async () => { @@ -28,6 +28,6 @@ describe('mount and destroy', () => { expect(content).not.toBeInTheDocument() await act() - expect(onDestroyed).toHaveBeenCalledOnce() + expect(onDestroyed).toHaveBeenCalledTimes(1) }) }) diff --git a/src/__tests__/rerender.test.js b/src/__tests__/rerender.test.js index ca4b8e8..21a782c 100644 --- a/src/__tests__/rerender.test.js +++ b/src/__tests__/rerender.test.js @@ -23,7 +23,7 @@ describe('rerender', () => { await rerender({ props: { name: 'Dolly' } }) expect(element).toHaveTextContent('Hello Dolly!') - expect(console.warn).toHaveBeenCalledOnce() + expect(console.warn).toHaveBeenCalledTimes(1) expect(console.warn).toHaveBeenCalledWith( expect.stringMatching(/deprecated/iu) ) From 326fb58aee4648523a88d94a60926e551dc56270 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Thu, 6 Jun 2024 09:38:41 -0400 Subject: [PATCH 06/37] chore(deps): update vite-plugin-svelte to disable `hmr` in test (#378) --- package.json | 2 +- vite.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b67db89..a82fa78 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ }, "devDependencies": { "@jest/globals": "^29.7.0", - "@sveltejs/vite-plugin-svelte": "^3.0.2", + "@sveltejs/vite-plugin-svelte": "^3.1.1", "@testing-library/jest-dom": "^6.3.0", "@testing-library/user-event": "^14.5.2", "@typescript-eslint/eslint-plugin": "7.8.0", diff --git a/vite.config.js b/vite.config.js index 293d426..b18c504 100644 --- a/vite.config.js +++ b/vite.config.js @@ -20,7 +20,7 @@ const alias = [ // https://vitejs.dev/config/ export default defineConfig({ - plugins: [svelte({ hot: false }), svelteTesting()], + plugins: [svelte(), svelteTesting()], test: { alias, environment: 'jsdom', From ee1c966c8d4ff7e8d0f0a80a19a086ae2b81983c Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Sat, 8 Jun 2024 23:26:34 -0400 Subject: [PATCH 07/37] ci(release): use conventionalcommits preset for release (#380) Closes #354, closes #356 --- .github/workflows/release.yml | 24 +++++++----------------- CONTRIBUTING.md | 15 ++++++++++++++- package.json | 5 +++-- release.config.js | 4 ++++ scripts/preview-release | 21 +++++++++++++++++++++ 5 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 release.config.js create mode 100755 scripts/preview-release diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 27af7a8..d63311e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,8 +69,8 @@ jobs: needs: main runs-on: ubuntu-latest if: ${{ github.repository == 'testing-library/svelte-testing-library' && - contains('refs/heads/main,refs/heads/beta,refs/heads/next,refs/heads/alpha', - github.ref) && github.event_name == 'push' }} + contains('refs/heads/main,refs/heads/next', github.ref) && + github.event_name == 'push' }} steps: - name: ⬇️ Checkout repo uses: actions/checkout@v4 @@ -78,24 +78,14 @@ jobs: - name: ⎔ Setup node uses: actions/setup-node@v4 with: - node-version: 16 - - - name: 📥 Download deps - run: npm install --no-package-lock + node-version: 20 - name: 🚀 Release - uses: cycjimmy/semantic-release-action@v2 + uses: cycjimmy/semantic-release-action@v4 with: - semantic_version: 17 - branches: | - [ - '+([0-9])?(.{+([0-9]),x}).x', - 'main', - 'next', - 'next-major', - {name: 'beta', prerelease: true}, - {name: 'alpha', prerelease: true} - ] + semantic_version: 24 + extra_plugins: | + conventional-changelog-conventionalcommits@8 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d5f35da..92856ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,10 +9,23 @@ ## Release -The module is released automatically from the `main` branch using [semantic-release-action][]. Version bumps and change logs are generated from the commit messages. +The module is released automatically from the `main` and `next` branches using [semantic-release-action][]. Version bumps and change logs are generated from the commit messages. [semantic-release-action]: https://github.com/cycjimmy/semantic-release-action +### Preview release + +If you would like to preview the release from a given branch, and... + +- You have push access to the repository +- The branch exists in GitHub + +...you can preview the next release version and changelog using: + +```shell +npm run preview-release +``` + ## Development setup After cloning the repository, install the project's dependencies and run the `validate` script to run all checks and tests to verify your setup. diff --git a/package.json b/package.json index a82fa78..c42f670 100644 --- a/package.json +++ b/package.json @@ -69,9 +69,10 @@ "test:vitest:happy-dom": "vitest run --coverage --environment happy-dom", "test:jest": "npx --node-options=\"--experimental-vm-modules --no-warnings\" jest --coverage", "types": "svelte-check", - "validate": "npm-run-all test:vitest:* types", + "validate": "npm-run-all test:vitest:* test:jest types", "contributors:add": "all-contributors add", - "contributors:generate": "all-contributors generate" + "contributors:generate": "all-contributors generate", + "preview-release": "./scripts/preview-release" }, "peerDependencies": { "svelte": "^3 || ^4 || ^5", diff --git a/release.config.js b/release.config.js new file mode 100644 index 0000000..7aeece8 --- /dev/null +++ b/release.config.js @@ -0,0 +1,4 @@ +export default { + preset: 'conventionalcommits', + branches: ['main', { name: 'next', prerelease: true }], +} diff --git a/scripts/preview-release b/scripts/preview-release new file mode 100755 index 0000000..82ebdee --- /dev/null +++ b/scripts/preview-release @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Preview the next release from a branch +# +# Prerequisites: +# - You must have push access to repository at the `origin` URL +# - The branch you are on must exist on `origin` + +set -euxo pipefail + +branch="$(git rev-parse --abbrev-ref HEAD)" +repository_url="$(git remote get-url origin)" + +npx \ + --package semantic-release@24 \ + --package conventional-changelog-conventionalcommits@8 \ + -- \ + semantic-release \ + --plugins="@semantic-release/commit-analyzer,@semantic-release/release-notes-generator" \ + --dry-run \ + --branches="$branch" \ + --repository-url="$repository_url" From 9dce164c7f1e0f1b012f7e4604352ac3bfd9eb98 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Thu, 20 Jun 2024 11:17:14 -0400 Subject: [PATCH 08/37] feat(svelte5): incorporate Svelte 5 support into main entry point (#375) --- .eslintrc.cjs | 2 + README.md | 20 +-- jest.config.js | 8 +- package.json | 4 +- src/__tests__/auto-cleanup.test.js | 9 +- src/__tests__/fixtures/Comp.svelte | 1 + src/__tests__/fixtures/CompRunes.svelte | 13 ++ src/__tests__/fixtures/Mounter.svelte | 2 +- src/__tests__/render.test.js | 14 +- src/__tests__/rerender.test.js | 26 +-- src/__tests__/utils.js | 17 ++ src/core/index.js | 27 ++++ src/core/legacy.js | 46 ++++++ src/core/modern.svelte.js | 50 ++++++ src/core/validate-options.js | 39 +++++ src/index.js | 8 +- src/pure.js | 200 +++++++++--------------- src/svelte5-index.js | 23 --- src/svelte5.js | 30 ---- vite.config.js | 16 -- 20 files changed, 311 insertions(+), 244 deletions(-) create mode 100644 src/__tests__/fixtures/CompRunes.svelte create mode 100644 src/core/index.js create mode 100644 src/core/legacy.js create mode 100644 src/core/modern.svelte.js create mode 100644 src/core/validate-options.js delete mode 100644 src/svelte5-index.js delete mode 100644 src/svelte5.js diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 778d507..326785a 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -25,6 +25,7 @@ module.exports = { }, rules: { 'no-undef-init': 'off', + 'prefer-const': 'off', }, }, { @@ -49,5 +50,6 @@ module.exports = { ecmaVersion: 2022, sourceType: 'module', }, + globals: { $state: 'readonly', $props: 'readonly' }, ignorePatterns: ['!/.*'], } diff --git a/README.md b/README.md index 51ca77a..12a1984 100644 --- a/README.md +++ b/README.md @@ -71,11 +71,11 @@ primary guiding principle is: This module is distributed via [npm][npm] which is bundled with [node][node] and should be installed as one of your project's `devDependencies`: -``` +```shell npm install --save-dev @testing-library/svelte ``` -This library has `peerDependencies` listings for `svelte >= 3`. +This library supports `svelte` versions `3`, `4`, and `5`. 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). @@ -102,22 +102,6 @@ See the [setup docs][] for more detailed setup instructions, including for other [vitest]: https://vitest.dev/ [setup docs]: https://testing-library.com/docs/svelte-testing-library/setup -### 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 add an alias to your `vite.config.js`: - -```js -export default defineConfig({ - plugins: [svelte(), svelteTesting()], - 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/jest.config.js b/jest.config.js index d6b1fde..8e78075 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,11 +1,12 @@ import { VERSION as SVELTE_VERSION } from 'svelte/compiler' -const IS_SVELTE_5 = SVELTE_VERSION >= '5' +const SVELTE_TRANSFORM_PATTERN = + SVELTE_VERSION >= '5' ? '^.+\\.svelte(?:\\.js)?$' : '^.+\\.svelte$' export default { testMatch: ['/src/__tests__/**/*.test.js'], transform: { - '^.+\\.svelte$': 'svelte-jester', + [SVELTE_TRANSFORM_PATTERN]: 'svelte-jester', }, moduleFileExtensions: ['js', 'svelte'], extensionsToTreatAsEsm: ['.svelte'], @@ -14,9 +15,6 @@ export default { injectGlobals: false, moduleNameMapper: { '^vitest$': '/src/__tests__/_jest-vitest-alias.js', - '^@testing-library/svelte$': IS_SVELTE_5 - ? '/src/svelte5-index.js' - : '/src/index.js', }, resetMocks: true, restoreMocks: true, diff --git a/package.json b/package.json index c42f670..a79dc52 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "./svelte5": { "types": "./types/index.d.ts", - "default": "./src/svelte5-index.js" + "default": "./src/index.js" }, "./vitest": { "default": "./src/vitest.js" @@ -120,7 +120,7 @@ "prettier-plugin-svelte": "3.2.3", "svelte": "^3 || ^4 || ^5", "svelte-check": "^3.6.3", - "svelte-jester": "^3.0.0", + "svelte-jester": "^5.0.0", "typescript": "^5.3.3", "vite": "^5.1.1", "vitest": "^1.5.2" diff --git a/src/__tests__/auto-cleanup.test.js b/src/__tests__/auto-cleanup.test.js index b06d120..803001e 100644 --- a/src/__tests__/auto-cleanup.test.js +++ b/src/__tests__/auto-cleanup.test.js @@ -1,10 +1,5 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' -import { IS_SVELTE_5 } from './utils.js' - -const importSvelteTestingLibrary = async () => - IS_SVELTE_5 ? import('../svelte5-index.js') : import('../index.js') - const globalAfterEach = vi.fn() describe('auto-cleanup', () => { @@ -19,7 +14,7 @@ describe('auto-cleanup', () => { }) test('calls afterEach with cleanup if globally defined', async () => { - const { render } = await importSvelteTestingLibrary() + const { render } = await import('../index.js') expect(globalAfterEach).toHaveBeenCalledTimes(1) expect(globalAfterEach).toHaveBeenLastCalledWith(expect.any(Function)) @@ -35,7 +30,7 @@ describe('auto-cleanup', () => { test('does not call afterEach if process STL_SKIP_AUTO_CLEANUP is set', async () => { process.env.STL_SKIP_AUTO_CLEANUP = 'true' - await importSvelteTestingLibrary() + await import('../index.js') expect(globalAfterEach).toHaveBeenCalledTimes(0) }) diff --git a/src/__tests__/fixtures/Comp.svelte b/src/__tests__/fixtures/Comp.svelte index ba23d88..86d8acd 100644 --- a/src/__tests__/fixtures/Comp.svelte +++ b/src/__tests__/fixtures/Comp.svelte @@ -1,3 +1,4 @@ + + +

Hello {name}!

+ + diff --git a/src/__tests__/fixtures/Mounter.svelte b/src/__tests__/fixtures/Mounter.svelte index 51ebcd8..27205dd 100644 --- a/src/__tests__/fixtures/Mounter.svelte +++ b/src/__tests__/fixtures/Mounter.svelte @@ -16,4 +16,4 @@ }) - diff --git a/src/__tests__/render.test.js b/src/__tests__/render.test.js index ea445d5..f396751 100644 --- a/src/__tests__/render.test.js +++ b/src/__tests__/render.test.js @@ -1,11 +1,15 @@ import { render } from '@testing-library/svelte' -import { describe, expect, test } from 'vitest' +import { beforeAll, describe, expect, test } from 'vitest' -import Comp from './fixtures/Comp.svelte' -import { IS_SVELTE_5 } from './utils.js' +import { COMPONENT_FIXTURES } from './utils.js' -describe('render', () => { +describe.each(COMPONENT_FIXTURES)('render ($mode)', ({ component }) => { const props = { name: 'World' } + let Comp + + beforeAll(async () => { + Comp = await import(component) + }) test('renders component into the document', () => { const { getByText } = render(Comp, { props }) @@ -65,7 +69,7 @@ describe('render', () => { expect(baseElement.firstChild).toBe(container) }) - test.skipIf(IS_SVELTE_5)('should accept anchor option in Svelte v4', () => { + test('should accept anchor option', () => { const baseElement = document.body const target = document.createElement('section') const anchor = document.createElement('div') diff --git a/src/__tests__/rerender.test.js b/src/__tests__/rerender.test.js index 21a782c..52acd0f 100644 --- a/src/__tests__/rerender.test.js +++ b/src/__tests__/rerender.test.js @@ -1,10 +1,15 @@ import { act, render, screen } from '@testing-library/svelte' -import { VERSION as SVELTE_VERSION } from 'svelte/compiler' -import { describe, expect, test, vi } from 'vitest' +import { beforeAll, describe, expect, test, vi } from 'vitest' -import Comp from './fixtures/Comp.svelte' +import { COMPONENT_FIXTURES, IS_SVELTE_5, MODE_RUNES } from './utils.js' + +describe.each(COMPONENT_FIXTURES)('rerender ($mode)', ({ mode, component }) => { + let Comp + + beforeAll(async () => { + Comp = await import(component) + }) -describe('rerender', () => { test('updates props', async () => { const { rerender } = render(Comp, { name: 'World' }) const element = screen.getByText('Hello World!') @@ -29,13 +34,12 @@ describe('rerender', () => { ) }) - test('change props with accessors', async () => { - const { component, getByText } = render( - Comp, - SVELTE_VERSION < '5' - ? { accessors: true, props: { name: 'World' } } - : { name: 'World' } - ) + test.skipIf(mode === MODE_RUNES)('change props with accessors', async () => { + const componentOptions = IS_SVELTE_5 + ? { name: 'World' } + : { accessors: true, props: { name: 'World' } } + + const { component, getByText } = render(Comp, componentOptions) const element = getByText('Hello World!') expect(element).toBeInTheDocument() diff --git a/src/__tests__/utils.js b/src/__tests__/utils.js index 69be184..68be33c 100644 --- a/src/__tests__/utils.js +++ b/src/__tests__/utils.js @@ -5,3 +5,20 @@ export const IS_JSDOM = window.navigator.userAgent.includes('jsdom') export const IS_HAPPYDOM = !IS_JSDOM // right now it's happy or js export const IS_SVELTE_5 = SVELTE_VERSION >= '5' + +export const MODE_LEGACY = 'legacy' + +export const MODE_RUNES = 'runes' + +export const COMPONENT_FIXTURES = [ + { + mode: MODE_LEGACY, + component: './fixtures/Comp.svelte', + isEnabled: true, + }, + { + mode: MODE_RUNES, + component: './fixtures/CompRunes.svelte', + isEnabled: IS_SVELTE_5, + }, +].filter(({ isEnabled }) => isEnabled) diff --git a/src/core/index.js b/src/core/index.js new file mode 100644 index 0000000..f4a40aa --- /dev/null +++ b/src/core/index.js @@ -0,0 +1,27 @@ +/** + * Rendering core for svelte-testing-library. + * + * Defines how components are added to and removed from the DOM. + * Will switch to legacy, class-based mounting logic + * if it looks like we're in a Svelte <= 4 environment. + */ +import * as LegacyCore from './legacy.js' +import * as ModernCore from './modern.svelte.js' +import { + createValidateOptions, + UnknownSvelteOptionsError, +} from './validate-options.js' + +const { mount, unmount, updateProps, allowedOptions } = + ModernCore.IS_MODERN_SVELTE ? ModernCore : LegacyCore + +/** Validate component options. */ +const validateOptions = createValidateOptions(allowedOptions) + +export { + mount, + UnknownSvelteOptionsError, + unmount, + updateProps, + validateOptions, +} diff --git a/src/core/legacy.js b/src/core/legacy.js new file mode 100644 index 0000000..c9e6d1c --- /dev/null +++ b/src/core/legacy.js @@ -0,0 +1,46 @@ +/** + * Legacy rendering core for svelte-testing-library. + * + * Supports Svelte <= 4. + */ + +/** Allowed options for the component constructor. */ +const allowedOptions = [ + 'target', + 'accessors', + 'anchor', + 'props', + 'hydrate', + 'intro', + 'context', +] + +/** + * Mount the component into the DOM. + * + * The `onDestroy` callback is included for strict backwards compatibility + * with previous versions of this library. It's mostly unnecessary logic. + */ +const mount = (Component, options, onDestroy) => { + const component = new Component(options) + + if (typeof onDestroy === 'function') { + component.$$.on_destroy.push(() => { + onDestroy(component) + }) + } + + return component +} + +/** Remove the component from the DOM. */ +const unmount = (component) => { + component.$destroy() +} + +/** Update the component's props. */ +const updateProps = (component, nextProps) => { + component.$set(nextProps) +} + +export { allowedOptions, mount, unmount, updateProps } diff --git a/src/core/modern.svelte.js b/src/core/modern.svelte.js new file mode 100644 index 0000000..3da78b4 --- /dev/null +++ b/src/core/modern.svelte.js @@ -0,0 +1,50 @@ +/** + * Modern rendering core for svelte-testing-library. + * + * Supports Svelte >= 5. + */ +import * as Svelte from 'svelte' + +/** Props signals for each rendered component. */ +const propsByComponent = new Map() + +/** Whether we're using Svelte >= 5. */ +const IS_MODERN_SVELTE = typeof Svelte.mount === 'function' + +/** Allowed options to the `mount` call. */ +const allowedOptions = [ + 'target', + 'anchor', + 'props', + 'events', + 'context', + 'intro', +] + +/** Mount the component into the DOM. */ +const mount = (Component, options) => { + const props = $state(options.props ?? {}) + const component = Svelte.mount(Component, { ...options, props }) + + propsByComponent.set(component, props) + + return component +} + +/** Remove the component from the DOM. */ +const unmount = (component) => { + propsByComponent.delete(component) + Svelte.unmount(component) +} + +/** + * Update the component's props. + * + * Relies on the `$state` signal added in `mount`. + */ +const updateProps = (component, nextProps) => { + const prevProps = propsByComponent.get(component) + Object.assign(prevProps, nextProps) +} + +export { allowedOptions, IS_MODERN_SVELTE, mount, unmount, updateProps } diff --git a/src/core/validate-options.js b/src/core/validate-options.js new file mode 100644 index 0000000..c0d794b --- /dev/null +++ b/src/core/validate-options.js @@ -0,0 +1,39 @@ +class UnknownSvelteOptionsError extends TypeError { + constructor(unknownOptions, allowedOptions) { + super(`Unknown options. + + Unknown: [ ${unknownOptions.join(', ')} ] + Allowed: [ ${allowedOptions.join(', ')} ] + + To pass both Svelte options and props to a component, + or to use props that share a name with a Svelte option, + you must place all your props under the \`props\` key: + + render(Component, { props: { /** props here **/ } }) +`) + this.name = 'UnknownSvelteOptionsError' + } +} + +const createValidateOptions = (allowedOptions) => (options) => { + const isProps = !Object.keys(options).some((option) => + allowedOptions.includes(option) + ) + + if (isProps) { + return { props: options } + } + + // Check if any props and Svelte options were accidentally mixed. + const unknownOptions = Object.keys(options).filter( + (option) => !allowedOptions.includes(option) + ) + + if (unknownOptions.length > 0) { + throw new UnknownSvelteOptionsError(unknownOptions, allowedOptions) + } + + return options +} + +export { createValidateOptions, UnknownSvelteOptionsError } diff --git a/src/index.js b/src/index.js index 2e3d772..3d8f18f 100644 --- a/src/index.js +++ b/src/index.js @@ -17,4 +17,10 @@ export * from '@testing-library/dom' // export svelte-specific functions and custom `fireEvent` // `fireEvent` must be a named export to take priority over wildcard export above -export { act, cleanup, fireEvent, render } from './pure.js' +export { + act, + cleanup, + fireEvent, + render, + UnknownSvelteOptionsError, +} from './pure.js' diff --git a/src/pure.js b/src/pure.js index 364c225..71dff1e 100644 --- a/src/pure.js +++ b/src/pure.js @@ -3,155 +3,105 @@ import { getQueriesForElement, prettyDOM, } from '@testing-library/dom' -import * as Svelte from 'svelte' -import { VERSION as SVELTE_VERSION } from 'svelte/compiler' - -const IS_SVELTE_5 = /^5\./.test(SVELTE_VERSION) - -export class SvelteTestingLibrary { - svelteComponentOptions = [ - 'target', - 'accessors', - 'anchor', - 'props', - 'hydrate', - 'intro', - 'context', - ] - - targetCache = new Set() - componentCache = new Set() - - checkProps(options) { - const isProps = !Object.keys(options).some((option) => - this.svelteComponentOptions.includes(option) - ) - - // Check if any props and Svelte options were accidentally mixed. - if (!isProps) { - const unrecognizedOptions = Object.keys(options).filter( - (option) => !this.svelteComponentOptions.includes(option) - ) - - if (unrecognizedOptions.length > 0) { - throw Error(` - Unknown options were found [${unrecognizedOptions}]. This might happen if you've mixed - passing in props with Svelte options into the render function. Valid Svelte options - are [${this.svelteComponentOptions}]. You can either change the prop names, or pass in your - props for that component via the \`props\` option.\n\n - Eg: const { /** Results **/ } = render(MyComponent, { props: { /** props here **/ } })\n\n - `) - } - - return options - } - - return { props: options } - } - - render(Component, componentOptions = {}, renderOptions = {}) { - componentOptions = this.checkProps(componentOptions) - - const baseElement = - renderOptions.baseElement ?? componentOptions.target ?? document.body - - const target = - componentOptions.target ?? - baseElement.appendChild(document.createElement('div')) - - this.targetCache.add(target) - - const ComponentConstructor = Component.default || Component - - const component = this.renderComponent(ComponentConstructor, { - ...componentOptions, - target, - }) - - return { - baseElement, - component, - container: target, - debug: (el = baseElement) => console.log(prettyDOM(el)), - rerender: async (props) => { - if (props.props) { - console.warn( - 'rerender({ props: {...} }) deprecated, use rerender({...}) instead' - ) - props = props.props - } - component.$set(props) - await Svelte.tick() - }, - unmount: () => { - this.cleanupComponent(component) - }, - ...getQueriesForElement(baseElement, renderOptions.queries), - } - } - - renderComponent(ComponentConstructor, componentOptions) { - if (IS_SVELTE_5) { - throw new Error('for Svelte 5, use `@testing-library/svelte/svelte5`') - } - - const component = new ComponentConstructor(componentOptions) +import { tick } from 'svelte' - this.componentCache.add(component) - - // TODO(mcous, 2024-02-11): remove this behavior in the next major version - component.$$.on_destroy.push(() => { - this.componentCache.delete(component) - }) +import { + mount, + UnknownSvelteOptionsError, + unmount, + updateProps, + validateOptions, +} from './core/index.js' + +const targetCache = new Set() +const componentCache = new Set() + +const render = (Component, options = {}, renderOptions = {}) => { + options = validateOptions(options) + + const baseElement = + renderOptions.baseElement ?? options.target ?? document.body + + const queries = getQueriesForElement(baseElement, renderOptions.queries) + + const target = + options.target ?? baseElement.appendChild(document.createElement('div')) + + targetCache.add(target) + + const component = mount( + Component.default ?? Component, + { ...options, target }, + cleanupComponent + ) + + componentCache.add(component) + + return { + baseElement, + component, + container: target, + debug: (el = baseElement) => { + console.log(prettyDOM(el)) + }, + rerender: async (props) => { + if (props.props) { + console.warn( + 'rerender({ props: {...} }) deprecated, use rerender({...}) instead' + ) + props = props.props + } - return component + updateProps(component, props) + await tick() + }, + unmount: () => { + cleanupComponent(component) + }, + ...queries, } +} - cleanupComponent(component) { - const inCache = this.componentCache.delete(component) +const cleanupComponent = (component) => { + const inCache = componentCache.delete(component) - if (inCache) { - component.$destroy() - } + if (inCache) { + unmount(component) } +} - cleanupTarget(target) { - const inCache = this.targetCache.delete(target) - - if (inCache && target.parentNode === document.body) { - document.body.removeChild(target) - } - } +const cleanupTarget = (target) => { + const inCache = targetCache.delete(target) - cleanup() { - this.componentCache.forEach(this.cleanupComponent.bind(this)) - this.targetCache.forEach(this.cleanupTarget.bind(this)) + if (inCache && target.parentNode === document.body) { + document.body.removeChild(target) } } -const instance = new SvelteTestingLibrary() - -export const render = instance.render.bind(instance) - -export const cleanup = instance.cleanup.bind(instance) +const cleanup = () => { + componentCache.forEach(cleanupComponent) + targetCache.forEach(cleanupTarget) +} -export const act = async (fn) => { +const act = async (fn) => { if (fn) { await fn() } - return Svelte.tick() + return tick() } -export const fireEvent = async (...args) => { +const fireEvent = async (...args) => { const event = dtlFireEvent(...args) - await Svelte.tick() + await tick() return event } Object.keys(dtlFireEvent).forEach((key) => { fireEvent[key] = async (...args) => { const event = dtlFireEvent[key](...args) - await Svelte.tick() + await tick() return event } }) + +export { act, cleanup, fireEvent, render, UnknownSvelteOptionsError } diff --git a/src/svelte5-index.js b/src/svelte5-index.js deleted file mode 100644 index ab49641..0000000 --- a/src/svelte5-index.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable import/export */ -import { act } from './pure.js' -import { cleanup } from './svelte5.js' - -// If we're running in a test runner that supports afterEach -// then we'll automatically run cleanup afterEach test -// this ensures that tests run in isolation from each other -// if you don't like this then either import the `pure` module -// or set the STL_SKIP_AUTO_CLEANUP env variable to 'true'. -if (typeof afterEach === 'function' && !process.env.STL_SKIP_AUTO_CLEANUP) { - afterEach(async () => { - await act() - cleanup() - }) -} - -// export all base queries, screen, etc. -export * from '@testing-library/dom' - -// export svelte-specific functions and custom `fireEvent` -// `fireEvent` must be a named export to take priority over wildcard export above -export { act, fireEvent } from './pure.js' -export { cleanup, render } from './svelte5.js' diff --git a/src/svelte5.js b/src/svelte5.js deleted file mode 100644 index a8dd494..0000000 --- a/src/svelte5.js +++ /dev/null @@ -1,30 +0,0 @@ -import { createClassComponent } from 'svelte/legacy' - -import { SvelteTestingLibrary } from './pure.js' - -class Svelte5TestingLibrary extends SvelteTestingLibrary { - svelteComponentOptions = [ - 'target', - 'props', - 'events', - 'context', - 'intro', - 'recover', - ] - - renderComponent(ComponentConstructor, componentOptions) { - const component = createClassComponent({ - ...componentOptions, - component: ComponentConstructor, - }) - - this.componentCache.add(component) - - return component - } -} - -const instance = new Svelte5TestingLibrary() - -export const render = instance.render.bind(instance) -export const cleanup = instance.cleanup.bind(instance) diff --git a/vite.config.js b/vite.config.js index b18c504..b165d41 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,28 +1,12 @@ -import path from 'node:path' - import { svelte } from '@sveltejs/vite-plugin-svelte' -import { VERSION as SVELTE_VERSION } from 'svelte/compiler' import { defineConfig } from 'vite' import { svelteTesting } from './src/vite.js' -const IS_SVELTE_5 = SVELTE_VERSION >= '5' - -const alias = [ - { - find: '@testing-library/svelte', - replacement: path.resolve( - __dirname, - IS_SVELTE_5 ? 'src/svelte5-index.js' : 'src/index.js' - ), - }, -] - // https://vitejs.dev/config/ export default defineConfig({ plugins: [svelte(), svelteTesting()], test: { - alias, environment: 'jsdom', setupFiles: ['./src/__tests__/_vitest-setup.js'], mockReset: true, From be82df14e76913b87967d5b7eb83eb97ca9c04ab Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Mon, 24 Jun 2024 15:09:25 -0400 Subject: [PATCH 09/37] docs(README): move links to sections that use them, remove unused links (#383) --- README.md | 119 +++++++++++++++++++++++++++++------------------------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 12a1984..bf45331 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,18 @@

Simple and complete Svelte testing utilities that encourage good testing practices.

-[**Read The Docs**](https://testing-library.com/docs/svelte-testing-library/intro) | -[Edit the docs](https://github.com/testing-library/testing-library-docs) +[**Read The Docs**][stl-docs] | [Edit the docs][stl-docs-repo] [![Build Status][build-badge]][build] [![Code Coverage][coverage-badge]][coverage] -[![version][version-badge]][package] [![downloads][downloads-badge]][npmtrends] +[![version][version-badge]][package] +[![downloads][downloads-badge]][downloads] [![MIT License][license-badge]][license] -[![All Contributors](https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square)](#contributors-) -[![PRs Welcome][prs-badge]][prs] [![Code of Conduct][coc-badge]][coc] +[![All Contributors][contributors-badge]][contributors] +[![PRs Welcome][prs-badge]][prs] +[![Code of Conduct][coc-badge]][coc] [![Discord][discord-badge]][discord] [![Watch on GitHub][github-watch-badge]][github-watch] @@ -33,6 +34,33 @@
+[stl-docs]: https://testing-library.com/docs/svelte-testing-library/intro +[stl-docs-repo]: https://github.com/testing-library/testing-library-docs +[build-badge]: https://img.shields.io/github/actions/workflow/status/testing-library/svelte-testing-library/release.yml?style=flat-square +[build]: https://github.com/testing-library/svelte-testing-library/actions +[coverage-badge]: https://img.shields.io/codecov/c/github/testing-library/svelte-testing-library.svg?style=flat-square +[coverage]: https://codecov.io/github/testing-library/svelte-testing-library +[version-badge]: https://img.shields.io/npm/v/@testing-library/svelte.svg?style=flat-square +[package]: https://www.npmjs.com/package/@testing-library/svelte +[downloads-badge]: https://img.shields.io/npm/dm/@testing-library/svelte.svg?style=flat-square +[downloads]: http://www.npmtrends.com/@testing-library/svelte +[license-badge]: https://img.shields.io/github/license/testing-library/svelte-testing-library?color=b&style=flat-square +[license]: https://github.com/testing-library/svelte-testing-library/blob/main/LICENSE +[contributors-badge]: https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square +[contributors]: #contributors +[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square +[prs]: http://makeapullrequest.com +[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square +[coc]: https://github.com/testing-library/svelte-testing-library/blob/main/CODE_OF_CONDUCT.md +[discord-badge]: https://img.shields.io/discord/723559267868737556.svg?color=7389D8&labelColor=6A7EC2&logo=discord&logoColor=ffffff&style=flat-square +[discord]: https://discord.gg/testing-library +[github-watch-badge]: https://img.shields.io/github/watchers/testing-library/svelte-testing-library.svg?style=social +[github-watch]: https://github.com/testing-library/svelte-testing-library/watchers +[github-star-badge]: https://img.shields.io/github/stars/testing-library/svelte-testing-library.svg?style=social +[github-star]: https://github.com/testing-library/svelte-testing-library/stargazers +[twitter]: https://twitter.com/intent/tweet?text=Check%20out%20svelte-testing-library%20by%20%40@TestingLib%20https%3A%2F%2Fgithub.com%2Ftesting-library%2Fsvelte-testing-library%20%F0%9F%91%8D +[twitter-badge]: https://img.shields.io/twitter/url/https/github.com/testing-library/svelte-testing-library.svg?style=social + ## Table of Contents @@ -41,31 +69,34 @@ - [The Problem](#the-problem) - [This Solution](#this-solution) - [Installation](#installation) +- [Setup](#setup) - [Docs](#docs) - [Issues](#issues) - [🐛 Bugs](#-bugs) - [💡 Feature Requests](#-feature-requests) - [❓ Questions](#-questions) - [Contributors](#contributors) -- [LICENSE](#license) ## The Problem -You want to write tests for your Svelte components so that they avoid including implementation -details, and are maintainable in the long run. +You want to write maintainable tests for your [Svelte][svelte] components. + +[svelte]: https://svelte.dev/ ## This Solution -The `svelte-testing-library` is a very lightweight solution for testing Svelte -components. It provides light utility functions on top of `svelte` and -`dom-testing-library`, in a way that encourages better testing practices. Its -primary guiding principle is: +`@testing-library/svelte` is a lightweight library for testing Svelte +components. It provides functions on top of `svelte` and +`@testing-library/dom` so you can mount Svelte components and query their +rendered output in the DOM. Its primary guiding principle is: > [The more your tests resemble the way your software is used, the more > confidence they can give you.][guiding-principle] +[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106 + ## Installation This module is distributed via [npm][npm] which is bundled with [node][node] and @@ -77,12 +108,18 @@ npm install --save-dev @testing-library/svelte This library supports `svelte` versions `3`, `4`, and `5`. -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). +You may also be interested in installing `@testing-library/jest-dom` so you can +use [the custom jest matchers][jest-dom]. + +[npm]: https://www.npmjs.com/ +[node]: https://nodejs.org +[jest-dom]: https://github.com/testing-library/jest-dom ## Setup -We recommend using `@testing-library/svelte` with [Vitest][] as your test runner. To get started, add the `svelteTesting` plugin to your Vite or Vitest config. +We recommend using `@testing-library/svelte` with [Vitest][] as your test +runner. To get started, add the `svelteTesting` plugin to your Vite or Vitest +config. ```diff // vite.config.js @@ -97,26 +134,31 @@ We recommend using `@testing-library/svelte` with [Vitest][] as your test runner }); ``` -See the [setup docs][] for more detailed setup instructions, including for other test runners like Jest. +See the [setup docs][] for more detailed setup instructions, including for other +test runners like Jest. [vitest]: https://vitest.dev/ [setup docs]: https://testing-library.com/docs/svelte-testing-library/setup ## Docs -See the [**docs**](https://testing-library.com/docs/svelte-testing-library/intro) over at the Testing Library website. +See the [**docs**][stl-docs] over at the Testing Library website. ## Issues _Looking to contribute? Look for the [Good First Issue][good-first-issue] label._ +[good-first-issue]: https://github.com/testing-library/svelte-testing-library/issues?utf8=✓&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3A"good+first+issue"+ + ### 🐛 Bugs Please file an issue for bugs, missing documentation, or unexpected behavior. [**See Bugs**][bugs] +[bugs]: https://github.com/testing-library/svelte-testing-library/issues?q=is%3Aissue+is%3Aopen+label%3Abug+sort%3Acreated-desc + ### 💡 Feature Requests Please file an issue to suggest new features. Vote on feature requests by adding @@ -124,6 +166,8 @@ a 👍. This helps maintainers prioritize what to work on. [**See Feature Requests**][requests] +[requests]: https://github.com/testing-library/svelte-testing-library/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc+label%3Aenhancement+is%3Aopen + ### ❓ Questions For questions related to using the library, please visit a support community @@ -132,6 +176,8 @@ instead of filing an issue on GitHub. - [Discord][discord] - [Stack Overflow][stackoverflow] +[stackoverflow]: https://stackoverflow.com/questions/tagged/svelte-testing-library + ## Contributors Thanks goes to these people ([emoji key][emojis]): @@ -173,44 +219,5 @@ Thanks goes to these people ([emoji key][emojis]): This project follows the [all-contributors][all-contributors] specification. Contributions of any kind welcome! -## LICENSE - -[MIT](LICENSE) - - - -[npm]: https://www.npmjs.com/ -[node]: https://nodejs.org -[build-badge]: https://img.shields.io/github/actions/workflow/status/testing-library/svelte-testing-library/release.yml?style=flat-square -[build]: https://github.com/testing-library/svelte-testing-library/actions -[coverage-badge]: https://img.shields.io/codecov/c/github/testing-library/svelte-testing-library.svg?style=flat-square -[coverage]: https://codecov.io/github/testing-library/svelte-testing-library -[version-badge]: https://img.shields.io/npm/v/@testing-library/svelte.svg?style=flat-square -[package]: https://www.npmjs.com/package/@testing-library/svelte -[downloads-badge]: https://img.shields.io/npm/dm/@testing-library/svelte.svg?style=flat-square -[npmtrends]: http://www.npmtrends.com/@testing-library/svelte -[discord-badge]: https://img.shields.io/discord/723559267868737556.svg?color=7389D8&labelColor=6A7EC2&logo=discord&logoColor=ffffff&style=flat-square -[discord]: https://discord.gg/testing-library -[license-badge]: https://img.shields.io/github/license/testing-library/svelte-testing-library?color=b&style=flat-square -[license]: https://github.com/testing-library/svelte-testing-library/blob/main/LICENSE -[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square -[prs]: http://makeapullrequest.com -[donate-badge]: https://img.shields.io/badge/$-support-green.svg?style=flat-square -[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square -[coc]: https://github.com/testing-library/svelte-testing-library/blob/main/CODE_OF_CONDUCT.md -[github-watch-badge]: https://img.shields.io/github/watchers/testing-library/svelte-testing-library.svg?style=social -[github-watch]: https://github.com/testing-library/svelte-testing-library/watchers -[github-star-badge]: https://img.shields.io/github/stars/testing-library/svelte-testing-library.svg?style=social -[github-star]: https://github.com/testing-library/svelte-testing-library/stargazers -[twitter]: https://twitter.com/intent/tweet?text=Check%20out%20svelte-testing-library%20by%20%40@TestingLib%20https%3A%2F%2Fgithub.com%2Ftesting-library%2Fsvelte-testing-library%20%F0%9F%91%8D -[twitter-badge]: https://img.shields.io/twitter/url/https/github.com/testing-library/svelte-testing-library.svg?style=social [emojis]: https://github.com/all-contributors/all-contributors#emoji-key [all-contributors]: https://github.com/all-contributors/all-contributors -[set-immediate]: https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate -[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106 -[bugs]: https://github.com/testing-library/svelte-testing-library/issues?q=is%3Aissue+is%3Aopen+label%3Abug+sort%3Acreated-desc -[requests]: https://github.com/testing-library/svelte-testing-library/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc+label%3Aenhancement+is%3Aopen -[good-first-issue]: https://github.com/testing-library/svelte-testing-library/issues?utf8=✓&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3A"good+first+issue"+ -[stackoverflow]: https://stackoverflow.com/questions/tagged/svelte-testing-library - - From a8f21f8402b736bac65a1361d075c0399542ff06 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Tue, 25 Jun 2024 21:41:54 -0400 Subject: [PATCH 10/37] fix(types): build types from JS source (#376) --- .eslintignore | 1 + .eslintrc.cjs | 1 + .github/workflows/release.yml | 34 ++++++++- .gitignore | 3 + package.json | 7 +- {types => src/__tests__}/types.test-d.ts | 36 +++++++++- src/index.js | 12 ++-- src/pure.js | 91 ++++++++++++++++++++---- tsconfig.build.json | 12 ++++ tsconfig.json | 3 +- types/index.d.ts | 82 --------------------- types/vite.d.ts | 12 ---- 12 files changed, 172 insertions(+), 122 deletions(-) rename {types => src/__tests__}/types.test-d.ts (67%) create mode 100644 tsconfig.build.json delete mode 100644 types/index.d.ts delete mode 100644 types/vite.d.ts diff --git a/.eslintignore b/.eslintignore index 111e490..39f9d9e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,3 +3,4 @@ scripts/* .prettierignore .github/workflows/* *.md +types diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 326785a..f403dca 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -26,6 +26,7 @@ module.exports = { rules: { 'no-undef-init': 'off', 'prefer-const': 'off', + 'svelte/no-unused-svelte-ignore': 'off', }, }, { diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d63311e..ef92dc0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -59,14 +59,38 @@ jobs: run: npm run test:${{ matrix.test-runner }} - name: ▶️ Run type-checks - if: ${{ matrix.node == '20' && matrix.svelte == '4' && matrix.test-runner == 'vitest:jsdom' }} + # NOTE: `SvelteComponent` is not generic in Svelte v3, so type-checking will not pass + if: ${{ matrix.node == '20' && matrix.svelte != '3' && matrix.test-runner == 'vitest:jsdom' }} run: npm run types - name: ⬆️ Upload coverage report uses: codecov/codecov-action@v3 + build: + runs-on: ubuntu-latest + steps: + - name: ⬇️ Checkout repo + uses: actions/checkout@v4 + + - name: ⎔ Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: 📥 Download deps + run: npm install --no-package-lock + + - name: 🏗️ Build types + run: npm run build + + - name: ⬆️ Upload types build + uses: actions/upload-artifact@v4 + with: + name: types + path: types + release: - needs: main + needs: [main, build] runs-on: ubuntu-latest if: ${{ github.repository == 'testing-library/svelte-testing-library' && contains('refs/heads/main,refs/heads/next', github.ref) && @@ -80,6 +104,12 @@ jobs: with: node-version: 20 + - name: 📥 Downloads types build + uses: actions/download-artifact@v4 + with: + name: types + path: types + - name: 🚀 Release uses: cycjimmy/semantic-release-action@v4 with: diff --git a/.gitignore b/.gitignore index c09be87..151e826 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ dist yarn-error.log package-lock.json yarn.lock + +# generated typing output +types diff --git a/package.json b/package.json index a79dc52..ebd9512 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "default": "./src/index.js" }, "./vitest": { + "types": "./types/vitest.d.ts", "default": "./src/vitest.js" }, "./vite": { @@ -26,7 +27,7 @@ "homepage": "https://github.com/testing-library/svelte-testing-library#readme", "repository": { "type": "git", - "url": "https://github.com/testing-library/svelte-testing-library" + "url": "git+https://github.com/testing-library/svelte-testing-library.git" }, "bugs": { "url": "https://github.com/testing-library/svelte-testing-library/issues" @@ -49,7 +50,6 @@ "files": [ "src", "types", - "!*.test-d.ts", "!__tests__" ], "scripts": { @@ -69,7 +69,8 @@ "test:vitest:happy-dom": "vitest run --coverage --environment happy-dom", "test:jest": "npx --node-options=\"--experimental-vm-modules --no-warnings\" jest --coverage", "types": "svelte-check", - "validate": "npm-run-all test:vitest:* test:jest types", + "validate": "npm-run-all test:vitest:* test:jest types build", + "build": "tsc -p tsconfig.build.json", "contributors:add": "all-contributors add", "contributors:generate": "all-contributors generate", "preview-release": "./scripts/preview-release" diff --git a/types/types.test-d.ts b/src/__tests__/types.test-d.ts similarity index 67% rename from types/types.test-d.ts rename to src/__tests__/types.test-d.ts index 4a42bb1..9927a05 100644 --- a/types/types.test-d.ts +++ b/src/__tests__/types.test-d.ts @@ -2,8 +2,8 @@ import { expectTypeOf } from 'expect-type' import type { ComponentProps, SvelteComponent } from 'svelte' import { describe, test } from 'vitest' -import Simple from '../src/__tests__/fixtures/Simple.svelte' -import * as subject from './index.js' +import * as subject from '../index.js' +import Simple from './fixtures/Simple.svelte' describe('types', () => { test('render is a function that accepts a Svelte component', () => { @@ -62,4 +62,36 @@ describe('types', () => { expectTypeOf(result.getByVibes).parameters.toMatchTypeOf<[vibes: string]>() }) + + test('act is an async function', () => { + expectTypeOf(subject.act).toMatchTypeOf<() => Promise>() + }) + + test('act accepts a sync function', () => { + expectTypeOf(subject.act).toMatchTypeOf<(fn: () => void) => Promise>() + }) + + test('act accepts an async function', () => { + expectTypeOf(subject.act).toMatchTypeOf< + (fn: () => Promise) => Promise + >() + }) + + test('fireEvent is an async function', () => { + expectTypeOf(subject.fireEvent).toMatchTypeOf< + ( + element: Element | Node | Document | Window, + event: Event + ) => Promise + >() + }) + + test('fireEvent[eventName] is an async function', () => { + expectTypeOf(subject.fireEvent.click).toMatchTypeOf< + ( + element: Element | Node | Document | Window, + options?: {} + ) => Promise + >() + }) }) diff --git a/src/index.js b/src/index.js index 3d8f18f..2704824 100644 --- a/src/index.js +++ b/src/index.js @@ -16,11 +16,7 @@ if (typeof afterEach === 'function' && !process.env.STL_SKIP_AUTO_CLEANUP) { export * from '@testing-library/dom' // export svelte-specific functions and custom `fireEvent` -// `fireEvent` must be a named export to take priority over wildcard export above -export { - act, - cleanup, - fireEvent, - render, - UnknownSvelteOptionsError, -} from './pure.js' +export { UnknownSvelteOptionsError } from './core/index.js' +export * from './pure.js' +// `fireEvent` must be named to take priority over wildcard from @testing-library/dom +export { fireEvent } from './pure.js' diff --git a/src/pure.js b/src/pure.js index 71dff1e..edb94b3 100644 --- a/src/pure.js +++ b/src/pure.js @@ -1,21 +1,61 @@ import { - fireEvent as dtlFireEvent, + fireEvent as baseFireEvent, getQueriesForElement, prettyDOM, } from '@testing-library/dom' import { tick } from 'svelte' -import { - mount, - UnknownSvelteOptionsError, - unmount, - updateProps, - validateOptions, -} from './core/index.js' +import { mount, unmount, updateProps, validateOptions } from './core/index.js' const targetCache = new Set() const componentCache = new Set() +/** + * Customize how Svelte renders the component. + * + * @template {import('svelte').SvelteComponent} C + * @typedef {import('svelte').ComponentProps | Partial>>} SvelteComponentOptions + */ + +/** + * Customize how Testing Library sets up the document and binds queries. + * + * @template {import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries] + * @typedef {{ + * baseElement?: HTMLElement + * queries?: Q + * }} RenderOptions + */ + +/** + * The rendered component and bound testing functions. + * + * @template {import('svelte').SvelteComponent} C + * @template {import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries] + * + * @typedef {{ + * container: HTMLElement + * baseElement: HTMLElement + * component: C + * debug: (el?: HTMLElement | DocumentFragment) => void + * rerender: (props: Partial>) => Promise + * unmount: () => void + * } & { + * [P in keyof Q]: import('@testing-library/dom').BoundFunction + * }} RenderResult + */ + +/** + * Render a component into the document. + * + * @template {import('svelte').SvelteComponent} C + * @template {import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries] + * + * @param {import('svelte').ComponentType} Component - The component to render. + * @param {SvelteComponentOptions} options - Customize how Svelte renders the component. + * @param {RenderOptions} renderOptions - Customize how Testing Library sets up the document and binds queries. + * @returns {RenderResult} The rendered component and bound testing functions. + */ const render = (Component, options = {}, renderOptions = {}) => { options = validateOptions(options) @@ -62,6 +102,7 @@ const render = (Component, options = {}, renderOptions = {}) => { } } +/** Remove a component from the component cache. */ const cleanupComponent = (component) => { const inCache = componentCache.delete(component) @@ -70,6 +111,7 @@ const cleanupComponent = (component) => { } } +/** Remove a target element from the target cache. */ const cleanupTarget = (target) => { const inCache = targetCache.delete(target) @@ -78,11 +120,18 @@ const cleanupTarget = (target) => { } } +/** Unmount all components and remove elements added to ``. */ const cleanup = () => { componentCache.forEach(cleanupComponent) targetCache.forEach(cleanupTarget) } +/** + * Call a function and wait for Svelte to flush pending changes. + * + * @param {() => unknown} [fn] - A function, which may be `async`, to call before flushing updates. + * @returns {Promise} + */ const act = async (fn) => { if (fn) { await fn() @@ -90,18 +139,36 @@ const act = async (fn) => { return tick() } +/** + * @typedef {(...args: Parameters) => Promise>} FireFunction + */ + +/** + * @typedef {{ + * [K in import('@testing-library/dom').EventType]: (...args: Parameters) => Promise> + * }} FireObject + */ + +/** + * Fire an event on an element. + * + * Consider using `@testing-library/user-event` instead, if possible. + * @see https://testing-library.com/docs/user-event/intro/ + * + * @type {FireFunction & FireObject} + */ const fireEvent = async (...args) => { - const event = dtlFireEvent(...args) + const event = baseFireEvent(...args) await tick() return event } -Object.keys(dtlFireEvent).forEach((key) => { +Object.keys(baseFireEvent).forEach((key) => { fireEvent[key] = async (...args) => { - const event = dtlFireEvent[key](...args) + const event = baseFireEvent[key](...args) await tick() return event } }) -export { act, cleanup, fireEvent, render, UnknownSvelteOptionsError } +export { act, cleanup, fireEvent, render } diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..0baa218 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": ["./tsconfig.json"], + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "noEmit": false, + "rootDir": "src", + "outDir": "types" + }, + "exclude": ["src/**/__tests__/**"] +} diff --git a/tsconfig.json b/tsconfig.json index 2b353f3..f79cace 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,11 @@ { "compilerOptions": { "module": "node16", + "allowJs": true, "noEmit": true, "skipLibCheck": true, "strict": true, "types": ["svelte", "vite/client", "vitest", "vitest/globals"] }, - "include": ["src", "types"] + "include": ["src"] } diff --git a/types/index.d.ts b/types/index.d.ts deleted file mode 100644 index a206467..0000000 --- a/types/index.d.ts +++ /dev/null @@ -1,82 +0,0 @@ -// Type definitions for Svelte Testing Library -// Project: https://github.com/testing-library/svelte-testing-library -// Definitions by: Rahim Alwer - -import { - BoundFunction, - EventType, - Queries, - queries, -} from '@testing-library/dom' -import { - ComponentConstructorOptions, - ComponentProps, - SvelteComponent, -} from 'svelte' - -export * from '@testing-library/dom' - -type SvelteComponentOptions = - | ComponentProps - | Partial>> - -type Constructor = new (...args: any[]) => T - -/** - * Render a Component into the Document. - */ -export type RenderResult< - C extends SvelteComponent, - Q extends Queries = typeof queries, -> = { - container: HTMLElement - baseElement: HTMLElement - component: C - debug: (el?: HTMLElement | DocumentFragment) => void - rerender: (props: Partial>) => Promise - unmount: () => void -} & { [P in keyof Q]: BoundFunction } - -export interface RenderOptions { - baseElement?: HTMLElement - queries?: Q -} - -export function render< - C extends SvelteComponent, - Q extends Queries = typeof queries, ->( - component: Constructor, - componentOptions?: SvelteComponentOptions, - renderOptions?: RenderOptions -): RenderResult - -/** - * Unmounts trees that were mounted with render. - */ -export function cleanup(): void - -/** - * Fires DOM events on an element provided by @testing-library/dom. Since Svelte needs to flush - * pending state changes via `tick`, these methods have been override and now return a promise. - */ -export type FireFunction = ( - element: Document | Element | Window, - event: Event -) => Promise - -export type FireObject = { - [K in EventType]: ( - element: Document | Element | Window, - options?: {} - ) => Promise -} - -export const fireEvent: FireFunction & FireObject - -/** - * Calls a function and notifies Svelte to flush any pending state changes. - * - * If the function returns a Promise, that Promise will be resolved first. - */ -export function act(fn?: () => unknown): Promise diff --git a/types/vite.d.ts b/types/vite.d.ts deleted file mode 100644 index 470e487..0000000 --- a/types/vite.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Plugin } from 'vite' - -/** - * Vite plugin to configure @testing-library/svelte. - * - * Ensures Svelte is imported correctly in tests - * and that the DOM is cleaned up after each test. - */ -export function svelteTesting(options?: { - resolveBrowser?: boolean - autoCleanup?: boolean -}): Plugin From 2fb7423e5b89b982da0297cbea990cb0ad683700 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Wed, 26 Jun 2024 11:23:04 -0400 Subject: [PATCH 11/37] fix(deps): allow svelte 5.0.0-next as peer dep (#384) --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ebd9512..47d6103 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "preview-release": "./scripts/preview-release" }, "peerDependencies": { - "svelte": "^3 || ^4 || ^5", + "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "vite": "*", "vitest": "*" }, @@ -119,7 +119,7 @@ "npm-run-all": "^4.1.5", "prettier": "3.2.5", "prettier-plugin-svelte": "3.2.3", - "svelte": "^3 || ^4 || ^5", + "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "svelte-check": "^3.6.3", "svelte-jester": "^5.0.0", "typescript": "^5.3.3", From 65ffbb755a86f28219bea8e10ceece7e1d14048e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 20:14:02 -0400 Subject: [PATCH 12/37] chore(deps): bump codecov/codecov-action from 3 to 4 in the actions group across 1 directory (#388) * chore(deps): bump codecov/codecov-action Bumps the actions group with 1 update in the / directory: [codecov/codecov-action](https://github.com/codecov/codecov-action). Updates `codecov/codecov-action` from 3 to 4 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: Michael Cousins --- .github/workflows/release.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ef92dc0..7b7ca01 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,6 +20,11 @@ jobs: name: Node ${{ matrix.node }}, Svelte ${{ matrix.svelte }}, ${{ matrix.test-runner }} runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} + + # enable OIDC for codecov uploads + permissions: + id-token: write + strategy: fail-fast: false matrix: @@ -64,7 +69,10 @@ jobs: run: npm run types - name: ⬆️ Upload coverage report - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 + with: + use_oidc: true + fail_ci_if_error: true build: runs-on: ubuntu-latest From 054bc0df571338d0d9be60387beac189e4c1d337 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 20:36:49 -0400 Subject: [PATCH 13/37] chore(deps-dev): bump the lint group across 1 directory with 6 updates (#387) Bumps the lint group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `7.8.0` | `7.14.1` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `7.8.0` | `7.14.1` | | [eslint-plugin-promise](https://github.com/eslint-community/eslint-plugin-promise) | `6.1.1` | `6.2.0` | | [eslint-plugin-svelte](https://github.com/sveltejs/eslint-plugin-svelte) | `2.38.0` | `2.41.0` | | [prettier](https://github.com/prettier/prettier) | `3.2.5` | `3.3.2` | | [prettier-plugin-svelte](https://github.com/sveltejs/prettier-plugin-svelte) | `3.2.3` | `3.2.5` | Updates `@typescript-eslint/eslint-plugin` from 7.8.0 to 7.14.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.14.1/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 7.8.0 to 7.14.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.14.1/packages/parser) Updates `eslint-plugin-promise` from 6.1.1 to 6.2.0 - [Release notes](https://github.com/eslint-community/eslint-plugin-promise/releases) - [Changelog](https://github.com/eslint-community/eslint-plugin-promise/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint-community/eslint-plugin-promise/compare/v6.1.1...v6.2.0) Updates `eslint-plugin-svelte` from 2.38.0 to 2.41.0 - [Release notes](https://github.com/sveltejs/eslint-plugin-svelte/releases) - [Commits](https://github.com/sveltejs/eslint-plugin-svelte/compare/v2.38.0...eslint-plugin-svelte@2.41.0) Updates `prettier` from 3.2.5 to 3.3.2 - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.2.5...3.3.2) Updates `prettier-plugin-svelte` from 3.2.3 to 3.2.5 - [Changelog](https://github.com/sveltejs/prettier-plugin-svelte/blob/master/CHANGELOG.md) - [Commits](https://github.com/sveltejs/prettier-plugin-svelte/compare/v3.2.3...v3.2.5) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint - dependency-name: eslint-plugin-promise dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint - dependency-name: eslint-plugin-svelte dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint - dependency-name: prettier-plugin-svelte dependency-type: direct:development update-type: version-update:semver-patch dependency-group: lint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 47d6103..b6a0c37 100644 --- a/package.json +++ b/package.json @@ -96,8 +96,8 @@ "@sveltejs/vite-plugin-svelte": "^3.1.1", "@testing-library/jest-dom": "^6.3.0", "@testing-library/user-event": "^14.5.2", - "@typescript-eslint/eslint-plugin": "7.8.0", - "@typescript-eslint/parser": "7.8.0", + "@typescript-eslint/eslint-plugin": "7.14.1", + "@typescript-eslint/parser": "7.14.1", "@vitest/coverage-v8": "^1.5.2", "all-contributors-cli": "^6.26.1", "doctoc": "^2.2.1", @@ -107,9 +107,9 @@ "eslint-plugin-import": "2.29.1", "eslint-plugin-json-files": "^4.1.0", "eslint-plugin-n": "16.6.2", - "eslint-plugin-promise": "6.1.1", + "eslint-plugin-promise": "6.2.0", "eslint-plugin-simple-import-sort": "12.1.0", - "eslint-plugin-svelte": "2.38.0", + "eslint-plugin-svelte": "2.41.0", "eslint-plugin-vitest-globals": "1.5.0", "expect-type": "^0.19.0", "happy-dom": "^14.7.1", @@ -117,8 +117,8 @@ "jest-environment-jsdom": "^29.7.0", "jsdom": "^24.0.0", "npm-run-all": "^4.1.5", - "prettier": "3.2.5", - "prettier-plugin-svelte": "3.2.3", + "prettier": "3.3.2", + "prettier-plugin-svelte": "3.2.5", "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "svelte-check": "^3.6.3", "svelte-jester": "^5.0.0", From d64de4a1bbbd7d31861272c26116cf25ea5497d1 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Fri, 28 Jun 2024 20:57:01 -0400 Subject: [PATCH 14/37] test(coverage): ensure proper files are included/excluded (#389) --- jest.config.js | 6 ++++++ vite.config.js | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 8e78075..4857b64 100644 --- a/jest.config.js +++ b/jest.config.js @@ -18,4 +18,10 @@ export default { }, resetMocks: true, restoreMocks: true, + collectCoverageFrom: ['/src/**/*'], + coveragePathIgnorePatterns: [ + '/__tests__/', + '/src/vite.js', + '/src/vitest.js', + ], } diff --git a/vite.config.js b/vite.config.js index b165d41..76baf61 100644 --- a/vite.config.js +++ b/vite.config.js @@ -13,7 +13,8 @@ export default defineConfig({ unstubGlobals: true, coverage: { provider: 'v8', - include: ['src'], + include: ['src/**/*'], + exclude: ['**/__tests__/**', 'src/vite.js', 'src/vitest.js'], }, }, }) From f5b7fe10fee20cb5a216c475c2efa7b369c5a24d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 22:02:42 -0400 Subject: [PATCH 15/37] chore(deps-dev): bump the lint group with 3 updates (#392) Bumps the lint group with 3 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [eslint-plugin-promise](https://github.com/eslint-community/eslint-plugin-promise). Updates `@typescript-eslint/eslint-plugin` from 7.14.1 to 7.15.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.15.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 7.14.1 to 7.15.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.15.0/packages/parser) Updates `eslint-plugin-promise` from 6.2.0 to 6.4.0 - [Release notes](https://github.com/eslint-community/eslint-plugin-promise/releases) - [Changelog](https://github.com/eslint-community/eslint-plugin-promise/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint-community/eslint-plugin-promise/compare/v6.2.0...v6.4.0) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint - dependency-name: eslint-plugin-promise dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index b6a0c37..c4924f5 100644 --- a/package.json +++ b/package.json @@ -96,8 +96,8 @@ "@sveltejs/vite-plugin-svelte": "^3.1.1", "@testing-library/jest-dom": "^6.3.0", "@testing-library/user-event": "^14.5.2", - "@typescript-eslint/eslint-plugin": "7.14.1", - "@typescript-eslint/parser": "7.14.1", + "@typescript-eslint/eslint-plugin": "7.15.0", + "@typescript-eslint/parser": "7.15.0", "@vitest/coverage-v8": "^1.5.2", "all-contributors-cli": "^6.26.1", "doctoc": "^2.2.1", @@ -107,7 +107,7 @@ "eslint-plugin-import": "2.29.1", "eslint-plugin-json-files": "^4.1.0", "eslint-plugin-n": "16.6.2", - "eslint-plugin-promise": "6.2.0", + "eslint-plugin-promise": "6.4.0", "eslint-plugin-simple-import-sort": "12.1.0", "eslint-plugin-svelte": "2.41.0", "eslint-plugin-vitest-globals": "1.5.0", From 75a3f389934d1c2c4af5a4c9e2dde6a2bfb1cd74 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Thu, 25 Jul 2024 19:05:04 -0400 Subject: [PATCH 16/37] fix(svelte5): synchronously flush changes after mount and unmount (#396) --- src/__tests__/mount.test.js | 5 +---- src/core/modern.svelte.js | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/__tests__/mount.test.js b/src/__tests__/mount.test.js index e25c429..df6351d 100644 --- a/src/__tests__/mount.test.js +++ b/src/__tests__/mount.test.js @@ -1,4 +1,4 @@ -import { act, render, screen } from '@testing-library/svelte' +import { render, screen } from '@testing-library/svelte' import { describe, expect, test, vi } from 'vitest' import Mounter from './fixtures/Mounter.svelte' @@ -14,20 +14,17 @@ describe('mount and destroy', () => { const content = screen.getByRole('button') expect(content).toBeInTheDocument() - await act() expect(onMounted).toHaveBeenCalledTimes(1) }) test('component is destroyed', async () => { const { unmount } = renderSubject() - await act() unmount() const content = screen.queryByRole('button') expect(content).not.toBeInTheDocument() - await act() expect(onDestroyed).toHaveBeenCalledTimes(1) }) }) diff --git a/src/core/modern.svelte.js b/src/core/modern.svelte.js index 3da78b4..34893f5 100644 --- a/src/core/modern.svelte.js +++ b/src/core/modern.svelte.js @@ -26,6 +26,7 @@ const mount = (Component, options) => { const props = $state(options.props ?? {}) const component = Svelte.mount(Component, { ...options, props }) + Svelte.flushSync() propsByComponent.set(component, props) return component @@ -34,7 +35,7 @@ const mount = (Component, options) => { /** Remove the component from the DOM. */ const unmount = (component) => { propsByComponent.delete(component) - Svelte.unmount(component) + Svelte.flushSync(() => Svelte.unmount(component)) } /** From 48ecc0b19f3cefcba73470ffc3c347be4348c07c Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Sat, 27 Jul 2024 11:56:57 -0400 Subject: [PATCH 17/37] ci: simplify main CI workflow matrix configuration (#393) --- .eslintrc.cjs | 10 ++------- .github/workflows/release.yml | 37 ++++++++++++-------------------- package.json | 40 +++++++++++++++++------------------ 3 files changed, 36 insertions(+), 51 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index f403dca..5bb87a1 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -3,14 +3,8 @@ module.exports = { env: { browser: true, es6: true, - 'vitest-globals/env': true, }, - extends: [ - 'standard', - 'plugin:vitest-globals/recommended', - 'plugin:svelte/recommended', - 'prettier', - ], + extends: ['standard', 'plugin:svelte/recommended', 'prettier'], plugins: ['svelte', 'simple-import-sort', 'json-files'], rules: { 'simple-import-sort/imports': 'error', @@ -51,6 +45,6 @@ module.exports = { ecmaVersion: 2022, sourceType: 'module', }, - globals: { $state: 'readonly', $props: 'readonly' }, + globals: { afterEach: 'readonly', $state: 'readonly', $props: 'readonly' }, ignorePatterns: ['!/.*'], } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b7ca01..9dbf325 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,9 +17,8 @@ jobs: main: # ignore all-contributors PRs if: ${{ !contains(github.head_ref, 'all-contributors') }} - name: Node ${{ matrix.node }}, Svelte ${{ matrix.svelte }}, ${{ matrix.test-runner }} + name: Node ${{ matrix.node }}, Svelte ${{ matrix.svelte }}, ${{ matrix.check }} runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental }} # enable OIDC for codecov uploads permissions: @@ -30,21 +29,17 @@ jobs: matrix: node: ['16', '18', '20'] svelte: ['3', '4'] - test-runner: ['vitest:jsdom', 'vitest:happy-dom', 'jest'] - experimental: [false] + check: ['test:vitest:jsdom', 'test:vitest:happy-dom', 'test:jest'] include: - - node: '20' - svelte: 'next' - test-runner: 'vitest:jsdom' - experimental: true - - node: '20' - svelte: 'next' - test-runner: 'vitest:happy-dom' - experimental: true - - node: '20' - svelte: 'next' - test-runner: 'jest' - experimental: true + # We only need to lint once, so do it on latest Node and Svelte + - { node: '20', svelte: '4', check: 'lint' } + # `SvelteComponent` is not generic in Svelte 3, so type-checking only passes in >= 4 + - { node: '20', svelte: '4', check: 'types' } + - { node: '20', svelte: 'next', check: 'types' } + # Only run Svelte 5 checks on latest Node + - { node: '20', svelte: 'next', check: 'test:vitest:jsdom' } + - { node: '20', svelte: 'next', check: 'test:vitest:happy-dom' } + - { node: '20', svelte: 'next', check: 'test:jest' } steps: - name: ⬇️ Checkout repo @@ -60,15 +55,11 @@ jobs: npm install --no-package-lock npm install --no-save svelte@${{ matrix.svelte }} - - name: ▶️ Run tests - run: npm run test:${{ matrix.test-runner }} - - - name: ▶️ Run type-checks - # NOTE: `SvelteComponent` is not generic in Svelte v3, so type-checking will not pass - if: ${{ matrix.node == '20' && matrix.svelte != '3' && matrix.test-runner == 'vitest:jsdom' }} - run: npm run types + - name: ▶️ Run ${{ matrix.check }} + run: npm run ${{ matrix.check }} - name: ⬆️ Upload coverage report + if: ${{ startsWith(matrix.check, 'test:') }} uses: codecov/codecov-action@v4 with: use_oidc: true diff --git a/package.json b/package.json index c4924f5..15fcd34 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "!__tests__" ], "scripts": { + "all": "npm-run-all contributors:generate toc format types build test:vitest:* test:jest", "toc": "doctoc README.md", "lint": "prettier . --check && eslint .", "lint:delta": "npm-run-all -p prettier:delta eslint:delta", @@ -62,7 +63,7 @@ "format:delta": "npm-run-all format:prettier:delta format:eslint:delta", "format:prettier:delta": "prettier --write `./scripts/changed-files`", "format:eslint:delta": "eslint --fix `./scripts/changed-files`", - "setup": "npm install && npm run validate", + "setup": "npm install && npm run all", "test": "vitest run --coverage", "test:watch": "vitest", "test:vitest:jsdom": "vitest run --coverage --environment jsdom", @@ -96,34 +97,33 @@ "@sveltejs/vite-plugin-svelte": "^3.1.1", "@testing-library/jest-dom": "^6.3.0", "@testing-library/user-event": "^14.5.2", - "@typescript-eslint/eslint-plugin": "7.15.0", - "@typescript-eslint/parser": "7.15.0", - "@vitest/coverage-v8": "^1.5.2", + "@typescript-eslint/eslint-plugin": "^7.16.0", + "@typescript-eslint/parser": "^7.16.0", + "@vitest/coverage-v8": "^2.0.2", "all-contributors-cli": "^6.26.1", "doctoc": "^2.2.1", - "eslint": "8.57.0", - "eslint-config-prettier": "9.1.0", - "eslint-config-standard": "17.1.0", - "eslint-plugin-import": "2.29.1", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.29.1", "eslint-plugin-json-files": "^4.1.0", - "eslint-plugin-n": "16.6.2", - "eslint-plugin-promise": "6.4.0", - "eslint-plugin-simple-import-sort": "12.1.0", - "eslint-plugin-svelte": "2.41.0", - "eslint-plugin-vitest-globals": "1.5.0", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.4.0", + "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-svelte": "^2.42.0", "expect-type": "^0.19.0", - "happy-dom": "^14.7.1", + "happy-dom": "^14.12.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "jsdom": "^24.0.0", "npm-run-all": "^4.1.5", - "prettier": "3.3.2", - "prettier-plugin-svelte": "3.2.5", + "prettier": "^3.3.3", + "prettier-plugin-svelte": "^3.2.5", "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", - "svelte-check": "^3.6.3", + "svelte-check": "^3.8.4", "svelte-jester": "^5.0.0", - "typescript": "^5.3.3", - "vite": "^5.1.1", - "vitest": "^1.5.2" + "typescript": "^5.5.3", + "vite": "^5.3.3", + "vitest": "^2.0.2" } } From 574c1e43d50bda29b599195d72ddfe0e0634b40e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Aug 2024 07:48:47 -0400 Subject: [PATCH 18/37] chore(deps-dev): bump the lint group with 2 updates (#397) * chore(deps-dev): bump the lint group with 2 updates Bumps the lint group with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser). Updates `@typescript-eslint/eslint-plugin` from 7.18.0 to 8.0.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.0.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 7.18.0 to 8.0.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.0.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-major dependency-group: lint - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-major dependency-group: lint ... Signed-off-by: dependabot[bot] Co-authored-by: Michael Cousins --- .eslintrc.cjs | 10 +--------- .github/dependabot.yml | 2 ++ package.json | 5 ++--- src/__tests__/types.test-d.ts | 1 + 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 5bb87a1..91f6920 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -5,7 +5,7 @@ module.exports = { es6: true, }, extends: ['standard', 'plugin:svelte/recommended', 'prettier'], - plugins: ['svelte', 'simple-import-sort', 'json-files'], + plugins: ['svelte', 'simple-import-sort'], rules: { 'simple-import-sort/imports': 'error', 'simple-import-sort/exports': 'error', @@ -31,14 +31,6 @@ module.exports = { 'plugin:@typescript-eslint/stylistic', 'prettier', ], - rules: { - '@typescript-eslint/ban-types': [ - 'error', - { types: { '{}': false }, extendDefaults: true }, - ], - '@typescript-eslint/no-explicit-any': 'off', - 'import/export': 'off', - }, }, ], parserOptions: { diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d4a16b5..c9eb8a7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -29,6 +29,8 @@ updates: versions: ['>=9'] - dependency-name: 'eslint-plugin-n' versions: ['>=17'] + - dependency-name: 'eslint-plugin-promise' + versions: ['>=7'] # Update GitHub Actions dependencies - package-ecosystem: 'github-actions' diff --git a/package.json b/package.json index 15fcd34..6581340 100644 --- a/package.json +++ b/package.json @@ -97,8 +97,8 @@ "@sveltejs/vite-plugin-svelte": "^3.1.1", "@testing-library/jest-dom": "^6.3.0", "@testing-library/user-event": "^14.5.2", - "@typescript-eslint/eslint-plugin": "^7.16.0", - "@typescript-eslint/parser": "^7.16.0", + "@typescript-eslint/eslint-plugin": "^8.0.0", + "@typescript-eslint/parser": "^8.0.0", "@vitest/coverage-v8": "^2.0.2", "all-contributors-cli": "^6.26.1", "doctoc": "^2.2.1", @@ -106,7 +106,6 @@ "eslint-config-prettier": "^9.1.0", "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-json-files": "^4.1.0", "eslint-plugin-n": "^16.6.2", "eslint-plugin-promise": "^6.4.0", "eslint-plugin-simple-import-sort": "^12.1.1", diff --git a/src/__tests__/types.test-d.ts b/src/__tests__/types.test-d.ts index 9927a05..077bbcd 100644 --- a/src/__tests__/types.test-d.ts +++ b/src/__tests__/types.test-d.ts @@ -90,6 +90,7 @@ describe('types', () => { expectTypeOf(subject.fireEvent.click).toMatchTypeOf< ( element: Element | Node | Document | Window, + // eslint-disable-next-line @typescript-eslint/no-empty-object-type options?: {} ) => Promise >() From 2cf781c3731009e7cd250891e1e7a2393910720c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:40:26 -0400 Subject: [PATCH 19/37] chore(deps-dev): bump the test group with 3 updates (#399) Bumps the test group with 3 updates: [expect-type](https://github.com/mmkal/expect-type), [happy-dom](https://github.com/capricorn86/happy-dom) and [jsdom](https://github.com/jsdom/jsdom). Updates `expect-type` from 0.19.0 to 0.20.0 - [Release notes](https://github.com/mmkal/expect-type/releases) - [Commits](https://github.com/mmkal/expect-type/compare/0.19.0...v0.20.0) Updates `happy-dom` from 14.12.3 to 15.7.3 - [Release notes](https://github.com/capricorn86/happy-dom/releases) - [Commits](https://github.com/capricorn86/happy-dom/compare/v14.12.3...v15.7.3) Updates `jsdom` from 24.1.3 to 25.0.0 - [Release notes](https://github.com/jsdom/jsdom/releases) - [Changelog](https://github.com/jsdom/jsdom/blob/main/Changelog.md) - [Commits](https://github.com/jsdom/jsdom/compare/24.1.3...25.0.0) --- updated-dependencies: - dependency-name: expect-type dependency-type: direct:development update-type: version-update:semver-minor dependency-group: test - dependency-name: happy-dom dependency-type: direct:development update-type: version-update:semver-major dependency-group: test - dependency-name: jsdom dependency-type: direct:development update-type: version-update:semver-major dependency-group: test ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 6581340..3fbcde7 100644 --- a/package.json +++ b/package.json @@ -110,11 +110,11 @@ "eslint-plugin-promise": "^6.4.0", "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-svelte": "^2.42.0", - "expect-type": "^0.19.0", - "happy-dom": "^14.12.3", + "expect-type": "^0.20.0", + "happy-dom": "^15.7.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "jsdom": "^24.0.0", + "jsdom": "^25.0.0", "npm-run-all": "^4.1.5", "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.2.5", From 6f45a962bffc649ef77afcc6a456f8a39df8cb0f Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Tue, 1 Oct 2024 14:46:50 -0400 Subject: [PATCH 20/37] fix(svelte5): update typings to support new component types (#400) --- .github/workflows/release.yml | 2 +- package.json | 1 + src/__tests__/fixtures/Mounter.svelte | 2 +- .../fixtures/{Simple.svelte => Typed.svelte} | 2 + src/__tests__/fixtures/TypedRunes.svelte | 8 ++++ src/__tests__/render-runes.test-d.ts | 39 +++++++++++++++++ ...s.test-d.ts => render-utilities.test-d.ts} | 43 +++---------------- src/__tests__/render.test-d.ts | 39 +++++++++++++++++ src/component-types.d.ts | 43 +++++++++++++++++++ src/pure.js | 14 +++--- tsconfig.legacy.json | 8 ++++ 11 files changed, 154 insertions(+), 47 deletions(-) rename src/__tests__/fixtures/{Simple.svelte => Typed.svelte} (76%) create mode 100644 src/__tests__/fixtures/TypedRunes.svelte create mode 100644 src/__tests__/render-runes.test-d.ts rename src/__tests__/{types.test-d.ts => render-utilities.test-d.ts} (55%) create mode 100644 src/__tests__/render.test-d.ts create mode 100644 src/component-types.d.ts create mode 100644 tsconfig.legacy.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9dbf325..8b2ac4a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: # We only need to lint once, so do it on latest Node and Svelte - { node: '20', svelte: '4', check: 'lint' } # `SvelteComponent` is not generic in Svelte 3, so type-checking only passes in >= 4 - - { node: '20', svelte: '4', check: 'types' } + - { node: '20', svelte: '4', check: 'types:legacy' } - { node: '20', svelte: 'next', check: 'types' } # Only run Svelte 5 checks on latest Node - { node: '20', svelte: 'next', check: 'test:vitest:jsdom' } diff --git a/package.json b/package.json index 3fbcde7..e536ce0 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "test:vitest:happy-dom": "vitest run --coverage --environment happy-dom", "test:jest": "npx --node-options=\"--experimental-vm-modules --no-warnings\" jest --coverage", "types": "svelte-check", + "types:legacy": "svelte-check --tsconfig tsconfig.legacy.json", "validate": "npm-run-all test:vitest:* test:jest types build", "build": "tsc -p tsconfig.build.json", "contributors:add": "all-contributors add", diff --git a/src/__tests__/fixtures/Mounter.svelte b/src/__tests__/fixtures/Mounter.svelte index 27205dd..68f72f9 100644 --- a/src/__tests__/fixtures/Mounter.svelte +++ b/src/__tests__/fixtures/Mounter.svelte @@ -16,4 +16,4 @@ }) - + diff --git a/src/__tests__/fixtures/Simple.svelte b/src/__tests__/fixtures/Typed.svelte similarity index 76% rename from src/__tests__/fixtures/Simple.svelte rename to src/__tests__/fixtures/Typed.svelte index c9c2f15..44960ab 100644 --- a/src/__tests__/fixtures/Simple.svelte +++ b/src/__tests__/fixtures/Typed.svelte @@ -1,6 +1,8 @@

hello {name}

diff --git a/src/__tests__/fixtures/TypedRunes.svelte b/src/__tests__/fixtures/TypedRunes.svelte new file mode 100644 index 0000000..979be41 --- /dev/null +++ b/src/__tests__/fixtures/TypedRunes.svelte @@ -0,0 +1,8 @@ + + +

hello {name}

+

count: {count}

diff --git a/src/__tests__/render-runes.test-d.ts b/src/__tests__/render-runes.test-d.ts new file mode 100644 index 0000000..2d0c69f --- /dev/null +++ b/src/__tests__/render-runes.test-d.ts @@ -0,0 +1,39 @@ +import { expectTypeOf } from 'expect-type' +import { describe, test } from 'vitest' + +import * as subject from '../index.js' +import Component from './fixtures/TypedRunes.svelte' + +describe('types', () => { + test('render is a function that accepts a Svelte component', () => { + subject.render(Component, { name: 'Alice', count: 42 }) + subject.render(Component, { props: { name: 'Alice', count: 42 } }) + }) + + test('rerender is a function that accepts partial props', async () => { + const { rerender } = subject.render(Component, { name: 'Alice', count: 42 }) + + await rerender({ name: 'Bob' }) + await rerender({ count: 0 }) + }) + + test('invalid prop types are rejected', () => { + // @ts-expect-error: name should be a string + subject.render(Component, { name: 42 }) + + // @ts-expect-error: name should be a string + subject.render(Component, { props: { name: 42 } }) + }) + + test('render result has container and component', () => { + const result = subject.render(Component, { name: 'Alice', count: 42 }) + + expectTypeOf(result).toMatchTypeOf<{ + container: HTMLElement + component: { hello: string } + debug: (el?: HTMLElement) => void + rerender: (props: { name?: string; count?: number }) => Promise + unmount: () => void + }>() + }) +}) diff --git a/src/__tests__/types.test-d.ts b/src/__tests__/render-utilities.test-d.ts similarity index 55% rename from src/__tests__/types.test-d.ts rename to src/__tests__/render-utilities.test-d.ts index 077bbcd..b45614e 100644 --- a/src/__tests__/types.test-d.ts +++ b/src/__tests__/render-utilities.test-d.ts @@ -1,45 +1,12 @@ import { expectTypeOf } from 'expect-type' -import type { ComponentProps, SvelteComponent } from 'svelte' import { describe, test } from 'vitest' import * as subject from '../index.js' -import Simple from './fixtures/Simple.svelte' - -describe('types', () => { - test('render is a function that accepts a Svelte component', () => { - subject.render(Simple, { name: 'Alice', count: 42 }) - subject.render(Simple, { props: { name: 'Alice', count: 42 } }) - }) - - test('rerender is a function that accepts partial props', async () => { - const { rerender } = subject.render(Simple, { name: 'Alice', count: 42 }) - - await rerender({ name: 'Bob' }) - await rerender({ count: 0 }) - }) - - test('invalid prop types are rejected', () => { - // @ts-expect-error: name should be a string - subject.render(Simple, { name: 42 }) - - // @ts-expect-error: name should be a string - subject.render(Simple, { props: { name: 42 } }) - }) - - test('render result has container and component', () => { - const result = subject.render(Simple, { name: 'Alice', count: 42 }) - - expectTypeOf(result).toMatchTypeOf<{ - container: HTMLElement - component: SvelteComponent<{ name: string }> - debug: (el?: HTMLElement) => void - rerender: (props: Partial>) => Promise - unmount: () => void - }>() - }) +import Component from './fixtures/Comp.svelte' +describe('render query and utility types', () => { test('render result has default queries', () => { - const result = subject.render(Simple, { name: 'Alice', count: 42 }) + const result = subject.render(Component, { name: 'Alice' }) expectTypeOf(result.getByRole).parameters.toMatchTypeOf< [role: subject.ByRoleMatcher, options?: subject.ByRoleOptions] @@ -55,8 +22,8 @@ describe('types', () => { () => '' ) const result = subject.render( - Simple, - { name: 'Alice', count: 42 }, + Component, + { name: 'Alice' }, { queries: { getByVibes } } ) diff --git a/src/__tests__/render.test-d.ts b/src/__tests__/render.test-d.ts new file mode 100644 index 0000000..f8d1d90 --- /dev/null +++ b/src/__tests__/render.test-d.ts @@ -0,0 +1,39 @@ +import { expectTypeOf } from 'expect-type' +import { describe, test } from 'vitest' + +import * as subject from '../index.js' +import Component from './fixtures/Typed.svelte' + +describe('types', () => { + test('render is a function that accepts a Svelte component', () => { + subject.render(Component, { name: 'Alice', count: 42 }) + subject.render(Component, { props: { name: 'Alice', count: 42 } }) + }) + + test('rerender is a function that accepts partial props', async () => { + const { rerender } = subject.render(Component, { name: 'Alice', count: 42 }) + + await rerender({ name: 'Bob' }) + await rerender({ count: 0 }) + }) + + test('invalid prop types are rejected', () => { + // @ts-expect-error: name should be a string + subject.render(Component, { name: 42 }) + + // @ts-expect-error: name should be a string + subject.render(Component, { props: { name: 42 } }) + }) + + test('render result has container and component', () => { + const result = subject.render(Component, { name: 'Alice', count: 42 }) + + expectTypeOf(result).toMatchTypeOf<{ + container: HTMLElement + component: { hello: string } + debug: (el?: HTMLElement) => void + rerender: (props: { name?: string; count?: number }) => Promise + unmount: () => void + }>() + }) +}) diff --git a/src/component-types.d.ts b/src/component-types.d.ts new file mode 100644 index 0000000..a349597 --- /dev/null +++ b/src/component-types.d.ts @@ -0,0 +1,43 @@ +import type * as Svelte from 'svelte' + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type IS_MODERN_SVELTE = any extends Svelte.Component ? false : true + +/** A compiled, imported Svelte component. */ +export type Component

= IS_MODERN_SVELTE extends true + ? Svelte.Component

| Svelte.SvelteComponent

+ : Svelte.SvelteComponent

+ +/** + * The type of an imported, compiled Svelte component. + * + * In Svelte 4, this was the Svelte component class' type. + * In Svelte 5, this distinction no longer matters. + */ +export type ComponentType = C extends Svelte.SvelteComponent + ? Svelte.ComponentType + : C + +/** The props of a component. */ +export type Props = Svelte.ComponentProps + +/** + * The exported fields of a component. + * + * In Svelte 4, this is simply the instance of the component class. + * In Svelte 5, this is the set of variables marked as `export`'d. + */ +export type Exports = C extends Svelte.SvelteComponent + ? C + : C extends Svelte.Component + ? E + : never + +/** + * Options that may be passed to `mount` when rendering the component. + * + * In Svelte 4, these are the options passed to the component constructor. + */ +export type MountOptions = IS_MODERN_SVELTE extends true + ? Parameters, Exports>>[1] + : Svelte.ComponentConstructorOptions> diff --git a/src/pure.js b/src/pure.js index edb94b3..875f87c 100644 --- a/src/pure.js +++ b/src/pure.js @@ -13,8 +13,8 @@ const componentCache = new Set() /** * Customize how Svelte renders the component. * - * @template {import('svelte').SvelteComponent} C - * @typedef {import('svelte').ComponentProps | Partial>>} SvelteComponentOptions + * @template {import('./component-types.js').Component} C + * @typedef {import('./component-types.js').Props | Partial>} SvelteComponentOptions */ /** @@ -30,15 +30,15 @@ const componentCache = new Set() /** * The rendered component and bound testing functions. * - * @template {import('svelte').SvelteComponent} C + * @template {import('./component-types.js').Component} C * @template {import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries] * * @typedef {{ * container: HTMLElement * baseElement: HTMLElement - * component: C + * component: import('./component-types.js').Exports * debug: (el?: HTMLElement | DocumentFragment) => void - * rerender: (props: Partial>) => Promise + * rerender: (props: Partial>) => Promise * unmount: () => void * } & { * [P in keyof Q]: import('@testing-library/dom').BoundFunction @@ -48,10 +48,10 @@ const componentCache = new Set() /** * Render a component into the document. * - * @template {import('svelte').SvelteComponent} C + * @template {import('./component-types.js').Component} C * @template {import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries] * - * @param {import('svelte').ComponentType} Component - The component to render. + * @param {import('./component-types.js').ComponentType} Component - The component to render. * @param {SvelteComponentOptions} options - Customize how Svelte renders the component. * @param {RenderOptions} renderOptions - Customize how Testing Library sets up the document and binds queries. * @returns {RenderResult} The rendered component and bound testing functions. diff --git a/tsconfig.legacy.json b/tsconfig.legacy.json new file mode 100644 index 0000000..b5d9e57 --- /dev/null +++ b/tsconfig.legacy.json @@ -0,0 +1,8 @@ +{ + "extends": ["./tsconfig.json"], + "exclude": [ + "src/__tests__/render-runes.test-d.ts", + "src/__tests__/fixtures/CompRunes.svelte", + "src/__tests__/fixtures/TypedRunes.svelte" + ] +} From f215ad81646382e0d67550ad11498e7e09cfc1bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:04:51 -0400 Subject: [PATCH 21/37] chore(deps-dev): bump svelte-check from 3.8.6 to 4.0.4 in the test group (#401) Bumps the test group with 1 update: [svelte-check](https://github.com/sveltejs/language-tools). Updates `svelte-check` from 3.8.6 to 4.0.4 - [Release notes](https://github.com/sveltejs/language-tools/releases) - [Commits](https://github.com/sveltejs/language-tools/compare/svelte-check-3.8.6...svelte-check-4.0.4) --- updated-dependencies: - dependency-name: svelte-check dependency-type: direct:development update-type: version-update:semver-major dependency-group: test ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e536ce0..3601cd8 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.2.5", "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", - "svelte-check": "^3.8.4", + "svelte-check": "^4.0.4", "svelte-jester": "^5.0.0", "typescript": "^5.5.3", "vite": "^5.3.3", From f4b1508eb58550b71802f59532025ff198cdb011 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Tue, 1 Oct 2024 22:43:41 -0400 Subject: [PATCH 22/37] fix(svelte5): ensure typings are actually included in the module (#402) --- package.json | 2 +- src/component-types.d.ts | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 3601cd8..192560d 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "types": "svelte-check", "types:legacy": "svelte-check --tsconfig tsconfig.legacy.json", "validate": "npm-run-all test:vitest:* test:jest types build", - "build": "tsc -p tsconfig.build.json", + "build": "tsc -p tsconfig.build.json && cp src/component-types.d.ts types", "contributors:add": "all-contributors add", "contributors:generate": "all-contributors generate", "preview-release": "./scripts/preview-release" diff --git a/src/component-types.d.ts b/src/component-types.d.ts index a349597..9df84c2 100644 --- a/src/component-types.d.ts +++ b/src/component-types.d.ts @@ -1,11 +1,14 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import type * as Svelte from 'svelte' -// eslint-disable-next-line @typescript-eslint/no-explicit-any type IS_MODERN_SVELTE = any extends Svelte.Component ? false : true /** A compiled, imported Svelte component. */ -export type Component

= IS_MODERN_SVELTE extends true - ? Svelte.Component

| Svelte.SvelteComponent

+export type Component< + P extends Record, + E extends Record, +> = IS_MODERN_SVELTE extends true + ? Svelte.Component | Svelte.SvelteComponent

: Svelte.SvelteComponent

/** @@ -19,7 +22,7 @@ export type ComponentType = C extends Svelte.SvelteComponent : C /** The props of a component. */ -export type Props = Svelte.ComponentProps +export type Props> = Svelte.ComponentProps /** * The exported fields of a component. @@ -29,7 +32,7 @@ export type Props = Svelte.ComponentProps */ export type Exports = C extends Svelte.SvelteComponent ? C - : C extends Svelte.Component + : C extends Svelte.Component ? E : never @@ -38,6 +41,7 @@ export type Exports = C extends Svelte.SvelteComponent * * In Svelte 4, these are the options passed to the component constructor. */ -export type MountOptions = IS_MODERN_SVELTE extends true - ? Parameters, Exports>>[1] - : Svelte.ComponentConstructorOptions> +export type MountOptions> = + IS_MODERN_SVELTE extends true + ? Parameters, Exports>>[1] + : Svelte.ComponentConstructorOptions> From b5e0f3d6ff7939b69ca31f6c58cec59560ab11a9 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Fri, 18 Oct 2024 12:12:44 -0400 Subject: [PATCH 23/37] fix(vite): set `ssr.noExternal` even if not in project package.json (#404) --- src/__tests__/utils.js | 2 + src/__tests__/vite-plugin.test.js | 227 ++++++++++++++++++++++++++++++ src/vite.js | 48 ++++++- vite.config.js | 1 + 4 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 src/__tests__/vite-plugin.test.js diff --git a/src/__tests__/utils.js b/src/__tests__/utils.js index 68be33c..f637d6f 100644 --- a/src/__tests__/utils.js +++ b/src/__tests__/utils.js @@ -4,6 +4,8 @@ export const IS_JSDOM = window.navigator.userAgent.includes('jsdom') export const IS_HAPPYDOM = !IS_JSDOM // right now it's happy or js +export const IS_JEST = Boolean(process.env.JEST_WORKER_ID) + export const IS_SVELTE_5 = SVELTE_VERSION >= '5' export const MODE_LEGACY = 'legacy' diff --git a/src/__tests__/vite-plugin.test.js b/src/__tests__/vite-plugin.test.js new file mode 100644 index 0000000..9232f65 --- /dev/null +++ b/src/__tests__/vite-plugin.test.js @@ -0,0 +1,227 @@ +import { beforeEach, describe, expect, test, vi } from 'vitest' + +import { svelteTesting } from '../vite.js' +import { IS_JEST } from './utils.js' + +describe.skipIf(IS_JEST)('vite plugin', () => { + beforeEach(() => { + vi.stubEnv('VITEST', '1') + }) + + test('does not modify config if disabled', () => { + const subject = svelteTesting({ + resolveBrowser: false, + autoCleanup: false, + noExternal: false, + }) + + const result = {} + subject.config(result) + + expect(result).toEqual({}) + }) + + test('does not modify config if not Vitest', () => { + vi.stubEnv('VITEST', '') + + const subject = svelteTesting() + + const result = {} + subject.config(result) + + expect(result).toEqual({}) + }) + + test.each([ + { + config: () => ({ resolve: { conditions: ['node'] } }), + expectedConditions: ['browser', 'node'], + }, + { + config: () => ({ resolve: { conditions: ['svelte', 'node'] } }), + expectedConditions: ['svelte', 'browser', 'node'], + }, + ])( + 'adds browser condition if necessary', + ({ config, expectedConditions }) => { + const subject = svelteTesting({ + resolveBrowser: true, + autoCleanup: false, + noExternal: false, + }) + + const result = config() + subject.config(result) + + expect(result).toEqual({ + resolve: { + conditions: expectedConditions, + }, + }) + } + ) + + test.each([ + { + config: () => ({}), + expectedConditions: [], + }, + { + config: () => ({ resolve: { conditions: [] } }), + expectedConditions: [], + }, + { + config: () => ({ resolve: { conditions: ['svelte'] } }), + expectedConditions: ['svelte'], + }, + ])( + 'skips browser condition if possible', + ({ config, expectedConditions }) => { + const subject = svelteTesting({ + resolveBrowser: true, + autoCleanup: false, + noExternal: false, + }) + + const result = config() + subject.config(result) + + expect(result).toEqual({ + resolve: { + conditions: expectedConditions, + }, + }) + } + ) + + test.each([ + { + config: () => ({}), + expectedSetupFiles: [expect.stringMatching(/src\/vitest.js$/u)], + }, + { + config: () => ({ test: { setupFiles: [] } }), + expectedSetupFiles: [expect.stringMatching(/src\/vitest.js$/u)], + }, + { + config: () => ({ test: { setupFiles: 'other-file.js' } }), + expectedSetupFiles: [ + 'other-file.js', + expect.stringMatching(/src\/vitest.js$/u), + ], + }, + ])('adds cleanup', ({ config, expectedSetupFiles }) => { + const subject = svelteTesting({ + resolveBrowser: false, + autoCleanup: true, + noExternal: false, + }) + + const result = config() + subject.config(result) + + expect(result).toEqual({ + test: { + setupFiles: expectedSetupFiles, + }, + }) + }) + + test('skips cleanup in global mode', () => { + const subject = svelteTesting({ + resolveBrowser: false, + autoCleanup: true, + noExternal: false, + }) + + const result = { test: { globals: true } } + subject.config(result) + + expect(result).toEqual({ + test: { + globals: true, + }, + }) + }) + + test.each([ + { + config: () => ({ ssr: { noExternal: [] } }), + expectedNoExternal: ['@testing-library/svelte'], + }, + { + config: () => ({}), + expectedNoExternal: ['@testing-library/svelte'], + }, + { + config: () => ({ ssr: { noExternal: 'other-file.js' } }), + expectedNoExternal: ['other-file.js', '@testing-library/svelte'], + }, + { + config: () => ({ ssr: { noExternal: /other/u } }), + expectedNoExternal: [/other/u, '@testing-library/svelte'], + }, + ])('adds noExternal rule', ({ config, expectedNoExternal }) => { + const subject = svelteTesting({ + resolveBrowser: false, + autoCleanup: false, + noExternal: true, + }) + + const result = config() + subject.config(result) + + expect(result).toEqual({ + ssr: { + noExternal: expectedNoExternal, + }, + }) + }) + + test.each([ + { + config: () => ({ ssr: { noExternal: true } }), + expectedNoExternal: true, + }, + { + config: () => ({ ssr: { noExternal: '@testing-library/svelte' } }), + expectedNoExternal: '@testing-library/svelte', + }, + { + config: () => ({ ssr: { noExternal: /svelte/u } }), + expectedNoExternal: /svelte/u, + }, + ])('skips noExternal if able', ({ config, expectedNoExternal }) => { + const subject = svelteTesting({ + resolveBrowser: false, + autoCleanup: false, + noExternal: true, + }) + + const result = config() + subject.config(result) + + expect(result).toEqual({ + ssr: { + noExternal: expectedNoExternal, + }, + }) + }) + + test('bails on noExternal if input is unexpected', () => { + const subject = svelteTesting({ + resolveBrowser: false, + autoCleanup: false, + noExternal: true, + }) + + const result = { ssr: { noExternal: false } } + subject.config(result) + + expect(result).toEqual({ + ssr: { + noExternal: false, + }, + }) + }) +}) diff --git a/src/vite.js b/src/vite.js index 0062b89..1ad712f 100644 --- a/src/vite.js +++ b/src/vite.js @@ -7,12 +7,13 @@ import { fileURLToPath } from 'node:url' * Ensures Svelte is imported correctly in tests * and that the DOM is cleaned up after each test. * - * @param {{resolveBrowser?: boolean, autoCleanup?: boolean}} options + * @param {{resolveBrowser?: boolean, autoCleanup?: boolean, noExternal?: boolean}} options * @returns {import('vite').Plugin} */ export const svelteTesting = ({ resolveBrowser = true, autoCleanup = true, + noExternal = true, } = {}) => ({ name: 'vite-plugin-svelte-testing-library', config: (config) => { @@ -27,6 +28,10 @@ export const svelteTesting = ({ if (autoCleanup) { addAutoCleanup(config) } + + if (noExternal) { + addNoExternal(config) + } }, }) @@ -64,6 +69,10 @@ const addAutoCleanup = (config) => { const test = config.test ?? {} let setupFiles = test.setupFiles ?? [] + if (test.globals) { + return + } + if (typeof setupFiles === 'string') { setupFiles = [setupFiles] } @@ -73,3 +82,40 @@ const addAutoCleanup = (config) => { test.setupFiles = setupFiles config.test = test } + +/** + * Add `@testing-library/svelte` to Vite's noExternal rules, if not present. + * + * This ensures `@testing-library/svelte` is processed by `@sveltejs/vite-plugin-svelte` + * in certain monorepo setups. + */ +const addNoExternal = (config) => { + const ssr = config.ssr ?? {} + let noExternal = ssr.noExternal ?? [] + + if (noExternal === true) { + return + } + + if (typeof noExternal === 'string' || noExternal instanceof RegExp) { + noExternal = [noExternal] + } + + if (!Array.isArray(noExternal)) { + return + } + + for (const rule of noExternal) { + if (typeof rule === 'string' && rule === '@testing-library/svelte') { + return + } + + if (rule instanceof RegExp && rule.test('@testing-library/svelte')) { + return + } + } + + noExternal.push('@testing-library/svelte') + ssr.noExternal = noExternal + config.ssr = ssr +} diff --git a/vite.config.js b/vite.config.js index 76baf61..1ddeea5 100644 --- a/vite.config.js +++ b/vite.config.js @@ -11,6 +11,7 @@ export default defineConfig({ setupFiles: ['./src/__tests__/_vitest-setup.js'], mockReset: true, unstubGlobals: true, + unstubEnvs: true, coverage: { provider: 'v8', include: ['src/**/*'], From 4b94019ac2f617f93b82b88a6de2b41d4d2918df Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Mon, 21 Oct 2024 20:06:09 -0400 Subject: [PATCH 24/37] ci: use production release of Svelte 5 by default (#405) --- .github/workflows/release.yml | 15 +++++++-------- package.json | 4 ++-- scripts/install-dependencies | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 10 deletions(-) create mode 100755 scripts/install-dependencies diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8b2ac4a..3f432da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,13 +33,14 @@ jobs: include: # We only need to lint once, so do it on latest Node and Svelte - { node: '20', svelte: '4', check: 'lint' } - # `SvelteComponent` is not generic in Svelte 3, so type-checking only passes in >= 4 + # Run type checks in latest node + - { node: '20', svelte: '3', check: 'types:legacy' } - { node: '20', svelte: '4', check: 'types:legacy' } - - { node: '20', svelte: 'next', check: 'types' } + - { node: '20', svelte: '5', check: 'types' } # Only run Svelte 5 checks on latest Node - - { node: '20', svelte: 'next', check: 'test:vitest:jsdom' } - - { node: '20', svelte: 'next', check: 'test:vitest:happy-dom' } - - { node: '20', svelte: 'next', check: 'test:jest' } + - { node: '20', svelte: '5', check: 'test:vitest:jsdom' } + - { node: '20', svelte: '5', check: 'test:vitest:happy-dom' } + - { node: '20', svelte: '5', check: 'test:jest' } steps: - name: ⬇️ Checkout repo @@ -51,9 +52,7 @@ jobs: node-version: ${{ matrix.node }} - name: 📥 Download deps - run: | - npm install --no-package-lock - npm install --no-save svelte@${{ matrix.svelte }} + run: ./scripts/install-dependencies ${{ matrix.svelte }} - name: ▶️ Run ${{ matrix.check }} run: npm run ${{ matrix.check }} diff --git a/package.json b/package.json index 192560d..0788f40 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ }, "devDependencies": { "@jest/globals": "^29.7.0", - "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0 || ^4.0.0", "@testing-library/jest-dom": "^6.3.0", "@testing-library/user-event": "^14.5.2", "@typescript-eslint/eslint-plugin": "^8.0.0", @@ -111,7 +111,7 @@ "eslint-plugin-promise": "^6.4.0", "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-svelte": "^2.42.0", - "expect-type": "^0.20.0", + "expect-type": "^1.1.0", "happy-dom": "^15.7.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", diff --git a/scripts/install-dependencies b/scripts/install-dependencies new file mode 100755 index 0000000..2a3f24f --- /dev/null +++ b/scripts/install-dependencies @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# Install dependencies for a given version of Svelte +set -euo pipefail + +svelte_version=${1} + +rm -rf node_modules +npm install --no-package-lock + +if [[ "${svelte_version}" == "4" ]]; then + npm uninstall --no-save @sveltejs/vite-plugin-svelte svelte + npm install --no-save @sveltejs/vite-plugin-svelte@3 svelte@4 +elif [[ "${svelte_version}" == "3" ]]; then + npm uninstall --no-save vite vitest @vitest/coverage-v8 @sveltejs/vite-plugin-svelte svelte-check svelte + npm install --no-save vite@4 vitest@1 @vitest/coverage-v8@1 @sveltejs/vite-plugin-svelte@2 svelte-check@3 svelte@3 +fi + +npm ls --depth=0 svelte From acbddfdf3556dd6f411361f9ccfd85c9773c7fdc Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Mon, 18 Nov 2024 16:55:03 -0500 Subject: [PATCH 25/37] fix(types): adjust legacy types for eslint-plugin-svelte (#409) --- .eslintrc.cjs | 7 +++++-- src/__tests__/render.test-d.ts | 11 +++++++++++ src/component-types.d.ts | 16 +++++++++------- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 91f6920..77f8b7c 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -26,9 +26,12 @@ module.exports = { { files: ['*.ts'], parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + }, extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:@typescript-eslint/stylistic', + 'plugin:@typescript-eslint/strict-type-checked', + 'plugin:@typescript-eslint/stylistic-type-checked', 'prettier', ], }, diff --git a/src/__tests__/render.test-d.ts b/src/__tests__/render.test-d.ts index f8d1d90..5b1e16a 100644 --- a/src/__tests__/render.test-d.ts +++ b/src/__tests__/render.test-d.ts @@ -1,4 +1,5 @@ import { expectTypeOf } from 'expect-type' +import { ComponentProps } from 'svelte' import { describe, test } from 'vitest' import * as subject from '../index.js' @@ -36,4 +37,14 @@ describe('types', () => { unmount: () => void }>() }) + + test('render function may be wrapped', () => { + const renderSubject = (props: ComponentProps) => { + return subject.render(Component, props) + } + + renderSubject({ name: 'Alice', count: 42 }) + // @ts-expect-error: name should be a string + renderSubject(Component, { name: 42 }) + }) }) diff --git a/src/component-types.d.ts b/src/component-types.d.ts index 9df84c2..2e4a5a5 100644 --- a/src/component-types.d.ts +++ b/src/component-types.d.ts @@ -1,7 +1,9 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-redundant-type-constituents */ import type * as Svelte from 'svelte' -type IS_MODERN_SVELTE = any extends Svelte.Component ? false : true +type IS_MODERN_SVELTE = Svelte.Component extends (...args: any[]) => any + ? true + : false /** A compiled, imported Svelte component. */ export type Component< @@ -14,12 +16,12 @@ export type Component< /** * The type of an imported, compiled Svelte component. * - * In Svelte 4, this was the Svelte component class' type. * In Svelte 5, this distinction no longer matters. + * In Svelte 4, this is the Svelte component class constructor. */ -export type ComponentType = C extends Svelte.SvelteComponent - ? Svelte.ComponentType - : C +export type ComponentType = IS_MODERN_SVELTE extends true + ? C + : new (...args: any[]) => C /** The props of a component. */ export type Props> = Svelte.ComponentProps @@ -27,8 +29,8 @@ export type Props> = Svelte.ComponentProps /** * The exported fields of a component. * - * In Svelte 4, this is simply the instance of the component class. * In Svelte 5, this is the set of variables marked as `export`'d. + * In Svelte 4, this is simply the instance of the component class. */ export type Exports = C extends Svelte.SvelteComponent ? C From 90ba912d4609d36168732a39e7fe6f99a35330a1 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Tue, 19 Nov 2024 12:20:36 -0500 Subject: [PATCH 26/37] fix(types): do not erase component type constraint (#410) --- .github/workflows/release.yml | 2 +- .gitignore | 1 + CONTRIBUTING.md | 17 +++++++++++++ package.json | 5 +++- src/__tests__/render.test-d.ts | 8 +++++++ src/component-types.d.ts | 44 +++++++++++++++++++++------------- 6 files changed, 59 insertions(+), 18 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f432da..1251be8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,7 +52,7 @@ jobs: node-version: ${{ matrix.node }} - name: 📥 Download deps - run: ./scripts/install-dependencies ${{ matrix.svelte }} + run: npm run install:${{ matrix.svelte }} - name: ▶️ Run ${{ matrix.check }} run: npm run ${{ matrix.check }} diff --git a/.gitignore b/.gitignore index 151e826..ca3410b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ public/bundle.* coverage dist .idea +*.tgz # These cause more harm than good when working with contributors yarn-error.log diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 92856ee..4c40705 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,6 +58,23 @@ npm test npm run test:watch ``` +### Using different versions of Svelte + +Use the provided script to set up your environment for different versions of Svelte: + +```shell +# install Svelte 5 +npm run install:5 + +# install Svelte 4 +npm run install:4 + +# install Svelte 3 +npm run install:3 +``` + +Not all checks will pass on `svelte<5`. Reference the CI workflows to see which checks are expected to pass on older versions. + ### Docs Use the `toc` script to ensure the README's table of contents is up to date: diff --git a/package.json b/package.json index 0788f40..b65e953 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,10 @@ "build": "tsc -p tsconfig.build.json && cp src/component-types.d.ts types", "contributors:add": "all-contributors add", "contributors:generate": "all-contributors generate", - "preview-release": "./scripts/preview-release" + "preview-release": "./scripts/preview-release", + "install:3": "./scripts/install-dependencies 3", + "install:4": "./scripts/install-dependencies 4", + "install:5": "./scripts/install-dependencies 5" }, "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", diff --git a/src/__tests__/render.test-d.ts b/src/__tests__/render.test-d.ts index 5b1e16a..08cad70 100644 --- a/src/__tests__/render.test-d.ts +++ b/src/__tests__/render.test-d.ts @@ -18,6 +18,14 @@ describe('types', () => { await rerender({ count: 0 }) }) + test('non-components are rejected', () => { + // eslint-disable-next-line @typescript-eslint/no-extraneous-class + class NotComponent {} + + // @ts-expect-error: component should be a Svelte component + subject.render(NotComponent) + }) + test('invalid prop types are rejected', () => { // @ts-expect-error: name should be a string subject.render(Component, { name: 42 }) diff --git a/src/component-types.d.ts b/src/component-types.d.ts index 2e4a5a5..9f707c2 100644 --- a/src/component-types.d.ts +++ b/src/component-types.d.ts @@ -1,17 +1,30 @@ /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-redundant-type-constituents */ -import type * as Svelte from 'svelte' +import type { + Component as ModernComponent, + ComponentConstructorOptions as LegacyConstructorOptions, + ComponentProps, + EventDispatcher, + mount, + SvelteComponent as LegacyComponent, + SvelteComponentTyped as Svelte3LegacyComponent, +} from 'svelte' -type IS_MODERN_SVELTE = Svelte.Component extends (...args: any[]) => any +type IS_MODERN_SVELTE = ModernComponent extends (...args: any[]) => any ? true : false +type IS_LEGACY_SVELTE_4 = + EventDispatcher extends (...args: any[]) => any ? true : false + /** A compiled, imported Svelte component. */ export type Component< - P extends Record, - E extends Record, + P extends Record = any, + E extends Record = any, > = IS_MODERN_SVELTE extends true - ? Svelte.Component | Svelte.SvelteComponent

- : Svelte.SvelteComponent

+ ? ModernComponent | LegacyComponent

+ : IS_LEGACY_SVELTE_4 extends true + ? LegacyComponent

+ : Svelte3LegacyComponent

/** * The type of an imported, compiled Svelte component. @@ -19,12 +32,12 @@ export type Component< * In Svelte 5, this distinction no longer matters. * In Svelte 4, this is the Svelte component class constructor. */ -export type ComponentType = IS_MODERN_SVELTE extends true - ? C - : new (...args: any[]) => C +export type ComponentType = C extends LegacyComponent + ? new (...args: any[]) => C + : C /** The props of a component. */ -export type Props> = Svelte.ComponentProps +export type Props = ComponentProps /** * The exported fields of a component. @@ -32,9 +45,9 @@ export type Props> = Svelte.ComponentProps * In Svelte 5, this is the set of variables marked as `export`'d. * In Svelte 4, this is simply the instance of the component class. */ -export type Exports = C extends Svelte.SvelteComponent +export type Exports = C extends LegacyComponent ? C - : C extends Svelte.Component + : C extends ModernComponent ? E : never @@ -43,7 +56,6 @@ export type Exports = C extends Svelte.SvelteComponent * * In Svelte 4, these are the options passed to the component constructor. */ -export type MountOptions> = - IS_MODERN_SVELTE extends true - ? Parameters, Exports>>[1] - : Svelte.ComponentConstructorOptions> +export type MountOptions = C extends LegacyComponent + ? LegacyConstructorOptions> + : Parameters, Exports>>[1] From f23466143ec986bb72ca8ac6dfdf78c2cd8f68a4 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Tue, 26 Nov 2024 14:50:13 -0800 Subject: [PATCH 27/37] ci: fix node 16 builds (#413) --- package.json | 9 +++++---- scripts/install-dependencies | 9 ++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index b65e953..cada7e0 100644 --- a/package.json +++ b/package.json @@ -103,9 +103,10 @@ "@testing-library/user-event": "^14.5.2", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", - "@vitest/coverage-v8": "^2.0.2", + "@vitest/coverage-v8": "^1.0.0 || ^2.0.2", "all-contributors-cli": "^6.26.1", "doctoc": "^2.2.1", + "esbuild": "*", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-config-standard": "^17.1.0", @@ -123,10 +124,10 @@ "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.2.5", "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", - "svelte-check": "^4.0.4", + "svelte-check": "^3.0.0 || ^4.0.4", "svelte-jester": "^5.0.0", "typescript": "^5.5.3", - "vite": "^5.3.3", - "vitest": "^2.0.2" + "vite": "^4.0.0 || ^5.3.3", + "vitest": "^1.0.0 || ^2.0.2" } } diff --git a/scripts/install-dependencies b/scripts/install-dependencies index 2a3f24f..029bfdf 100755 --- a/scripts/install-dependencies +++ b/scripts/install-dependencies @@ -15,4 +15,11 @@ elif [[ "${svelte_version}" == "3" ]]; then npm install --no-save vite@4 vitest@1 @vitest/coverage-v8@1 @sveltejs/vite-plugin-svelte@2 svelte-check@3 svelte@3 fi -npm ls --depth=0 svelte +npm dedupe +installed_version=$(npm ls --depth=0 --parseable svelte@${svelte_version}) + +if [[ -z "${installed_version}" ]]; then + echo "Error: expected svelte@${svelte_version}" + npm ls --depth=0 svelte + exit 1 +fi From 7f86754782ae44d1a94c1f2ce5c4b32b784ab7d1 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Sun, 8 Dec 2024 13:12:14 -0500 Subject: [PATCH 28/37] test(jest): skip auto-cleanup tests in Jest (#417) --- src/__tests__/auto-cleanup.test.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/__tests__/auto-cleanup.test.js b/src/__tests__/auto-cleanup.test.js index 803001e..7962925 100644 --- a/src/__tests__/auto-cleanup.test.js +++ b/src/__tests__/auto-cleanup.test.js @@ -1,8 +1,13 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' -const globalAfterEach = vi.fn() +import { IS_JEST } from './utils.js' + +// TODO(mcous, 2024-12-08): clearing module cache and re-importing +// in Jest breaks Svelte's environment checking heuristics. +// Re-implement this test in a more accurate environment, without mocks. +describe.skipIf(IS_JEST)('auto-cleanup', () => { + const globalAfterEach = vi.fn() -describe('auto-cleanup', () => { beforeEach(() => { vi.resetModules() globalThis.afterEach = globalAfterEach From 234cc6deee60622dc6b6185b342703b2bdd08464 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:14:38 -0500 Subject: [PATCH 29/37] chore(deps): bump codecov/codecov-action from 4 to 5 in the actions group (#414) chore(deps): bump codecov/codecov-action in the actions group Bumps the actions group with 1 update: [codecov/codecov-action](https://github.com/codecov/codecov-action). Updates `codecov/codecov-action` from 4 to 5 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1251be8..79dbe5a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -59,7 +59,7 @@ jobs: - name: ⬆️ Upload coverage report if: ${{ startsWith(matrix.check, 'test:') }} - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: use_oidc: true fail_ci_if_error: true From 298d7ee7765fe46b809fc8fa8ffeb6f9fb525e9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 00:24:57 -0500 Subject: [PATCH 30/37] chore(deps-dev): bump happy-dom from 15.11.7 to 16.3.0 in the test group (#420) Bumps the test group with 1 update: [happy-dom](https://github.com/capricorn86/happy-dom). Updates `happy-dom` from 15.11.7 to 16.3.0 - [Release notes](https://github.com/capricorn86/happy-dom/releases) - [Commits](https://github.com/capricorn86/happy-dom/compare/v15.11.7...v16.3.0) --- updated-dependencies: - dependency-name: happy-dom dependency-type: direct:development update-type: version-update:semver-major dependency-group: test ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cada7e0..008ee04 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-svelte": "^2.42.0", "expect-type": "^1.1.0", - "happy-dom": "^15.7.3", + "happy-dom": "^16.3.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "jsdom": "^25.0.0", From 5aa405a6d8a6ccb8e04e6530f1ade509b5b3cce0 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Tue, 11 Feb 2025 12:50:42 -0500 Subject: [PATCH 31/37] ci: fix node 16 builds again (#423) --- package.json | 4 ++-- scripts/install-dependencies | 18 ++++++++++++------ vite.config.js | 7 +++++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 008ee04..77bba08 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "@testing-library/user-event": "^14.5.2", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", - "@vitest/coverage-v8": "^1.0.0 || ^2.0.2", + "@vitest/coverage-v8": "0.x.x || ^1.0.0 || ^2.0.2", "all-contributors-cli": "^6.26.1", "doctoc": "^2.2.1", "esbuild": "*", @@ -128,6 +128,6 @@ "svelte-jester": "^5.0.0", "typescript": "^5.5.3", "vite": "^4.0.0 || ^5.3.3", - "vitest": "^1.0.0 || ^2.0.2" + "vitest": "0.x.x || ^1.0.0 || ^2.0.2" } } diff --git a/scripts/install-dependencies b/scripts/install-dependencies index 029bfdf..8f403a0 100755 --- a/scripts/install-dependencies +++ b/scripts/install-dependencies @@ -1,22 +1,28 @@ #!/usr/bin/env bash # Install dependencies for a given version of Svelte -set -euo pipefail +set -euxo pipefail svelte_version=${1} +node_version=$(node --version) rm -rf node_modules npm install --no-package-lock -if [[ "${svelte_version}" == "4" ]]; then +if [[ "${svelte_version}" == "3" ]]; then + npm uninstall --no-save vite vitest @vitest/coverage-v8 @sveltejs/vite-plugin-svelte svelte-check svelte + npm install --no-save vite@4 vitest@0.x.x @vitest/coverage-v8@0.x.x @sveltejs/vite-plugin-svelte@2 svelte-check@3 svelte@3 +elif [[ "${svelte_version}" == "4" ]]; then npm uninstall --no-save @sveltejs/vite-plugin-svelte svelte npm install --no-save @sveltejs/vite-plugin-svelte@3 svelte@4 -elif [[ "${svelte_version}" == "3" ]]; then - npm uninstall --no-save vite vitest @vitest/coverage-v8 @sveltejs/vite-plugin-svelte svelte-check svelte - npm install --no-save vite@4 vitest@1 @vitest/coverage-v8@1 @sveltejs/vite-plugin-svelte@2 svelte-check@3 svelte@3 +fi + +if [[ "${node_version}" =~ "v16" ]]; then + npm uninstall --no-save vite vitest @vitest/coverage-v8 @sveltejs/vite-plugin-svelte + npm install --no-save vite@4 vitest@0.x.x @vitest/coverage-v8@0.x.x @sveltejs/vite-plugin-svelte@2 fi npm dedupe -installed_version=$(npm ls --depth=0 --parseable svelte@${svelte_version}) +installed_version=$(npm ls --depth=0 --parseable svelte@${svelte_version}) if [[ -z "${installed_version}" ]]; then echo "Error: expected svelte@${svelte_version}" diff --git a/vite.config.js b/vite.config.js index 1ddeea5..2db8a40 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,8 +1,12 @@ +import { createRequire } from 'node:module' + import { svelte } from '@sveltejs/vite-plugin-svelte' import { defineConfig } from 'vite' import { svelteTesting } from './src/vite.js' +const require = createRequire(import.meta.url) + // https://vitejs.dev/config/ export default defineConfig({ plugins: [svelte(), svelteTesting()], @@ -17,5 +21,8 @@ export default defineConfig({ include: ['src/**/*'], exclude: ['**/__tests__/**', 'src/vite.js', 'src/vitest.js'], }, + alias: { + '@testing-library/svelte': require.resolve('.'), + }, }, }) From e86dadae6c55899b13415a71a506a44f414305e1 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Tue, 11 Feb 2025 12:59:23 -0500 Subject: [PATCH 32/37] fix(types): fix types when using legacy components in Svelte 5 (#424) --- src/__tests__/fixtures/Typed.svelte | 5 +++++ src/__tests__/render-runes.test-d.ts | 32 +++++++++++++++++++++++++++- src/component-types.d.ts | 14 ++++++------ 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/__tests__/fixtures/Typed.svelte b/src/__tests__/fixtures/Typed.svelte index 44960ab..383214f 100644 --- a/src/__tests__/fixtures/Typed.svelte +++ b/src/__tests__/fixtures/Typed.svelte @@ -1,9 +1,14 @@

hello {name}

count: {count}

+ diff --git a/src/__tests__/render-runes.test-d.ts b/src/__tests__/render-runes.test-d.ts index 2d0c69f..48d9d27 100644 --- a/src/__tests__/render-runes.test-d.ts +++ b/src/__tests__/render-runes.test-d.ts @@ -1,7 +1,8 @@ import { expectTypeOf } from 'expect-type' -import { describe, test } from 'vitest' +import { describe, test, vi } from 'vitest' import * as subject from '../index.js' +import LegacyComponent from './fixtures/Typed.svelte' import Component from './fixtures/TypedRunes.svelte' describe('types', () => { @@ -37,3 +38,32 @@ describe('types', () => { }>() }) }) + +describe('legacy component types', () => { + test('render accepts events', () => { + const onGreeting = vi.fn() + subject.render(LegacyComponent, { + props: { name: 'Alice', count: 42 }, + events: { greeting: onGreeting }, + }) + }) + + test('component $set and $on are not allowed', () => { + const onGreeting = vi.fn() + const { component } = subject.render(LegacyComponent, { + name: 'Alice', + count: 42, + }) + + expectTypeOf(component).toMatchTypeOf<{ hello: string }>() + + // @ts-expect-error: Svelte 5 mount does not return `$set` + component.$on('greeting', onGreeting) + + // @ts-expect-error: Svelte 5 mount does not return `$set` + component.$set({ name: 'Bob' }) + + // @ts-expect-error: Svelte 5 mount does not return `$destroy` + component.$destroy() + }) +}) diff --git a/src/component-types.d.ts b/src/component-types.d.ts index 9f707c2..8300243 100644 --- a/src/component-types.d.ts +++ b/src/component-types.d.ts @@ -45,17 +45,17 @@ export type Props = ComponentProps * In Svelte 5, this is the set of variables marked as `export`'d. * In Svelte 4, this is simply the instance of the component class. */ -export type Exports = C extends LegacyComponent - ? C - : C extends ModernComponent +export type Exports = IS_MODERN_SVELTE extends true + ? C extends ModernComponent ? E - : never + : C & { $set: never; $on: never; $destroy: never } + : C /** * Options that may be passed to `mount` when rendering the component. * * In Svelte 4, these are the options passed to the component constructor. */ -export type MountOptions = C extends LegacyComponent - ? LegacyConstructorOptions> - : Parameters, Exports>>[1] +export type MountOptions = IS_MODERN_SVELTE extends true + ? Parameters, Exports>>[1] + : LegacyConstructorOptions> From b2005b75128c3d45dad96b860558ac21cf85bf66 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Wed, 7 May 2025 11:35:21 -0400 Subject: [PATCH 33/37] test: replace deprecated `toMatchTypeOf` (#434) --- package.json | 1 + src/__tests__/render-runes.test-d.ts | 4 ++-- src/__tests__/render-utilities.test-d.ts | 14 +++++++------- src/__tests__/render.test-d.ts | 2 +- tsconfig.json | 3 ++- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 77bba08..8bab118 100644 --- a/package.json +++ b/package.json @@ -127,6 +127,7 @@ "svelte-check": "^3.0.0 || ^4.0.4", "svelte-jester": "^5.0.0", "typescript": "^5.5.3", + "typescript-svelte-plugin": "^0.3.46", "vite": "^4.0.0 || ^5.3.3", "vitest": "0.x.x || ^1.0.0 || ^2.0.2" } diff --git a/src/__tests__/render-runes.test-d.ts b/src/__tests__/render-runes.test-d.ts index 48d9d27..51b7cfa 100644 --- a/src/__tests__/render-runes.test-d.ts +++ b/src/__tests__/render-runes.test-d.ts @@ -29,7 +29,7 @@ describe('types', () => { test('render result has container and component', () => { const result = subject.render(Component, { name: 'Alice', count: 42 }) - expectTypeOf(result).toMatchTypeOf<{ + expectTypeOf(result).toExtend<{ container: HTMLElement component: { hello: string } debug: (el?: HTMLElement) => void @@ -55,7 +55,7 @@ describe('legacy component types', () => { count: 42, }) - expectTypeOf(component).toMatchTypeOf<{ hello: string }>() + expectTypeOf(component).toExtend<{ hello: string }>() // @ts-expect-error: Svelte 5 mount does not return `$set` component.$on('greeting', onGreeting) diff --git a/src/__tests__/render-utilities.test-d.ts b/src/__tests__/render-utilities.test-d.ts index b45614e..d1e3869 100644 --- a/src/__tests__/render-utilities.test-d.ts +++ b/src/__tests__/render-utilities.test-d.ts @@ -8,7 +8,7 @@ describe('render query and utility types', () => { test('render result has default queries', () => { const result = subject.render(Component, { name: 'Alice' }) - expectTypeOf(result.getByRole).parameters.toMatchTypeOf< + expectTypeOf(result.getByRole).parameters.toExtend< [role: subject.ByRoleMatcher, options?: subject.ByRoleOptions] >() }) @@ -27,25 +27,25 @@ describe('render query and utility types', () => { { queries: { getByVibes } } ) - expectTypeOf(result.getByVibes).parameters.toMatchTypeOf<[vibes: string]>() + expectTypeOf(result.getByVibes).parameters.toExtend<[vibes: string]>() }) test('act is an async function', () => { - expectTypeOf(subject.act).toMatchTypeOf<() => Promise>() + expectTypeOf(subject.act).toExtend<() => Promise>() }) test('act accepts a sync function', () => { - expectTypeOf(subject.act).toMatchTypeOf<(fn: () => void) => Promise>() + expectTypeOf(subject.act).toExtend<(fn: () => void) => Promise>() }) test('act accepts an async function', () => { - expectTypeOf(subject.act).toMatchTypeOf< + expectTypeOf(subject.act).toExtend< (fn: () => Promise) => Promise >() }) test('fireEvent is an async function', () => { - expectTypeOf(subject.fireEvent).toMatchTypeOf< + expectTypeOf(subject.fireEvent).toExtend< ( element: Element | Node | Document | Window, event: Event @@ -54,7 +54,7 @@ describe('render query and utility types', () => { }) test('fireEvent[eventName] is an async function', () => { - expectTypeOf(subject.fireEvent.click).toMatchTypeOf< + expectTypeOf(subject.fireEvent.click).toExtend< ( element: Element | Node | Document | Window, // eslint-disable-next-line @typescript-eslint/no-empty-object-type diff --git a/src/__tests__/render.test-d.ts b/src/__tests__/render.test-d.ts index 08cad70..bf0a611 100644 --- a/src/__tests__/render.test-d.ts +++ b/src/__tests__/render.test-d.ts @@ -37,7 +37,7 @@ describe('types', () => { test('render result has container and component', () => { const result = subject.render(Component, { name: 'Alice', count: 42 }) - expectTypeOf(result).toMatchTypeOf<{ + expectTypeOf(result).toExtend<{ container: HTMLElement component: { hello: string } debug: (el?: HTMLElement) => void diff --git a/tsconfig.json b/tsconfig.json index f79cace..0cc0fdf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,8 @@ "noEmit": true, "skipLibCheck": true, "strict": true, - "types": ["svelte", "vite/client", "vitest", "vitest/globals"] + "types": ["svelte", "vite/client", "vitest", "vitest/globals"], + "plugins": [{ "name": "typescript-svelte-plugin" }] }, "include": ["src"] } From ea37c470b02233baca598834143c3ac27f669b9d Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Wed, 7 May 2025 12:17:32 -0400 Subject: [PATCH 34/37] test: extract `tests` directory out of `src` (#433) --- jest.config.js | 8 ++++---- package.json | 3 +-- src/__tests__/utils.js => tests/_env.js | 0 {src/__tests__ => tests}/_jest-setup.js | 0 {src/__tests__ => tests}/_jest-vitest-alias.js | 0 {src/__tests__ => tests}/_vitest-setup.js | 0 {src/__tests__ => tests}/act.test.js | 0 {src/__tests__ => tests}/auto-cleanup.test.js | 6 +++--- {src/__tests__ => tests}/cleanup.test.js | 0 {src/__tests__ => tests}/context.test.js | 0 {src/__tests__ => tests}/debug.test.js | 0 {src/__tests__ => tests}/events.test.js | 0 {src/__tests__ => tests}/fixtures/Comp.svelte | 0 {src/__tests__ => tests}/fixtures/CompRunes.svelte | 0 {src/__tests__ => tests}/fixtures/Context.svelte | 0 {src/__tests__ => tests}/fixtures/Mounter.svelte | 0 {src/__tests__ => tests}/fixtures/Transitioner.svelte | 0 {src/__tests__ => tests}/fixtures/Typed.svelte | 0 {src/__tests__ => tests}/fixtures/TypedRunes.svelte | 0 {src/__tests__ => tests}/mount.test.js | 0 {src/__tests__ => tests}/multi-base.test.js | 0 {src/__tests__ => tests}/render-runes.test-d.ts | 2 +- {src/__tests__ => tests}/render-utilities.test-d.ts | 2 +- {src/__tests__ => tests}/render.test-d.ts | 2 +- {src/__tests__ => tests}/render.test.js | 2 +- {src/__tests__ => tests}/rerender.test.js | 2 +- {src/__tests__ => tests}/transition.test.js | 2 +- {src/__tests__ => tests}/vite-plugin.test.js | 4 ++-- tsconfig.build.json | 2 +- tsconfig.json | 6 +++++- tsconfig.legacy.json | 6 +++--- vite.config.js | 9 ++++----- 32 files changed, 29 insertions(+), 27 deletions(-) rename src/__tests__/utils.js => tests/_env.js (100%) rename {src/__tests__ => tests}/_jest-setup.js (100%) rename {src/__tests__ => tests}/_jest-vitest-alias.js (100%) rename {src/__tests__ => tests}/_vitest-setup.js (100%) rename {src/__tests__ => tests}/act.test.js (100%) rename {src/__tests__ => tests}/auto-cleanup.test.js (89%) rename {src/__tests__ => tests}/cleanup.test.js (100%) rename {src/__tests__ => tests}/context.test.js (100%) rename {src/__tests__ => tests}/debug.test.js (100%) rename {src/__tests__ => tests}/events.test.js (100%) rename {src/__tests__ => tests}/fixtures/Comp.svelte (100%) rename {src/__tests__ => tests}/fixtures/CompRunes.svelte (100%) rename {src/__tests__ => tests}/fixtures/Context.svelte (100%) rename {src/__tests__ => tests}/fixtures/Mounter.svelte (100%) rename {src/__tests__ => tests}/fixtures/Transitioner.svelte (100%) rename {src/__tests__ => tests}/fixtures/Typed.svelte (100%) rename {src/__tests__ => tests}/fixtures/TypedRunes.svelte (100%) rename {src/__tests__ => tests}/mount.test.js (100%) rename {src/__tests__ => tests}/multi-base.test.js (100%) rename {src/__tests__ => tests}/render-runes.test-d.ts (97%) rename {src/__tests__ => tests}/render-utilities.test-d.ts (97%) rename {src/__tests__ => tests}/render.test-d.ts (97%) rename {src/__tests__ => tests}/render.test.js (98%) rename {src/__tests__ => tests}/rerender.test.js (95%) rename {src/__tests__ => tests}/transition.test.js (94%) rename {src/__tests__ => tests}/vite-plugin.test.js (98%) diff --git a/jest.config.js b/jest.config.js index 4857b64..ff518c4 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,23 +4,23 @@ const SVELTE_TRANSFORM_PATTERN = SVELTE_VERSION >= '5' ? '^.+\\.svelte(?:\\.js)?$' : '^.+\\.svelte$' export default { - testMatch: ['/src/__tests__/**/*.test.js'], + testMatch: ['/tests/**/*.test.js'], transform: { [SVELTE_TRANSFORM_PATTERN]: 'svelte-jester', }, moduleFileExtensions: ['js', 'svelte'], extensionsToTreatAsEsm: ['.svelte'], testEnvironment: 'jsdom', - setupFilesAfterEnv: ['/src/__tests__/_jest-setup.js'], + setupFilesAfterEnv: ['/tests/_jest-setup.js'], injectGlobals: false, moduleNameMapper: { - '^vitest$': '/src/__tests__/_jest-vitest-alias.js', + '^vitest$': '/tests/_jest-vitest-alias.js', + '^@testing-library\\/svelte$': '/src/index.js', }, resetMocks: true, restoreMocks: true, collectCoverageFrom: ['/src/**/*'], coveragePathIgnorePatterns: [ - '/__tests__/', '/src/vite.js', '/src/vitest.js', ], diff --git a/package.json b/package.json index 8bab118..d686b2c 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,7 @@ ], "files": [ "src", - "types", - "!__tests__" + "types" ], "scripts": { "all": "npm-run-all contributors:generate toc format types build test:vitest:* test:jest", diff --git a/src/__tests__/utils.js b/tests/_env.js similarity index 100% rename from src/__tests__/utils.js rename to tests/_env.js diff --git a/src/__tests__/_jest-setup.js b/tests/_jest-setup.js similarity index 100% rename from src/__tests__/_jest-setup.js rename to tests/_jest-setup.js diff --git a/src/__tests__/_jest-vitest-alias.js b/tests/_jest-vitest-alias.js similarity index 100% rename from src/__tests__/_jest-vitest-alias.js rename to tests/_jest-vitest-alias.js diff --git a/src/__tests__/_vitest-setup.js b/tests/_vitest-setup.js similarity index 100% rename from src/__tests__/_vitest-setup.js rename to tests/_vitest-setup.js diff --git a/src/__tests__/act.test.js b/tests/act.test.js similarity index 100% rename from src/__tests__/act.test.js rename to tests/act.test.js diff --git a/src/__tests__/auto-cleanup.test.js b/tests/auto-cleanup.test.js similarity index 89% rename from src/__tests__/auto-cleanup.test.js rename to tests/auto-cleanup.test.js index 7962925..d6ba28b 100644 --- a/src/__tests__/auto-cleanup.test.js +++ b/tests/auto-cleanup.test.js @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' -import { IS_JEST } from './utils.js' +import { IS_JEST } from './_env.js' // TODO(mcous, 2024-12-08): clearing module cache and re-importing // in Jest breaks Svelte's environment checking heuristics. @@ -19,7 +19,7 @@ describe.skipIf(IS_JEST)('auto-cleanup', () => { }) test('calls afterEach with cleanup if globally defined', async () => { - const { render } = await import('../index.js') + const { render } = await import('@testing-library/svelte') expect(globalAfterEach).toHaveBeenCalledTimes(1) expect(globalAfterEach).toHaveBeenLastCalledWith(expect.any(Function)) @@ -35,7 +35,7 @@ describe.skipIf(IS_JEST)('auto-cleanup', () => { test('does not call afterEach if process STL_SKIP_AUTO_CLEANUP is set', async () => { process.env.STL_SKIP_AUTO_CLEANUP = 'true' - await import('../index.js') + await import('@testing-library/svelte') expect(globalAfterEach).toHaveBeenCalledTimes(0) }) diff --git a/src/__tests__/cleanup.test.js b/tests/cleanup.test.js similarity index 100% rename from src/__tests__/cleanup.test.js rename to tests/cleanup.test.js diff --git a/src/__tests__/context.test.js b/tests/context.test.js similarity index 100% rename from src/__tests__/context.test.js rename to tests/context.test.js diff --git a/src/__tests__/debug.test.js b/tests/debug.test.js similarity index 100% rename from src/__tests__/debug.test.js rename to tests/debug.test.js diff --git a/src/__tests__/events.test.js b/tests/events.test.js similarity index 100% rename from src/__tests__/events.test.js rename to tests/events.test.js diff --git a/src/__tests__/fixtures/Comp.svelte b/tests/fixtures/Comp.svelte similarity index 100% rename from src/__tests__/fixtures/Comp.svelte rename to tests/fixtures/Comp.svelte diff --git a/src/__tests__/fixtures/CompRunes.svelte b/tests/fixtures/CompRunes.svelte similarity index 100% rename from src/__tests__/fixtures/CompRunes.svelte rename to tests/fixtures/CompRunes.svelte diff --git a/src/__tests__/fixtures/Context.svelte b/tests/fixtures/Context.svelte similarity index 100% rename from src/__tests__/fixtures/Context.svelte rename to tests/fixtures/Context.svelte diff --git a/src/__tests__/fixtures/Mounter.svelte b/tests/fixtures/Mounter.svelte similarity index 100% rename from src/__tests__/fixtures/Mounter.svelte rename to tests/fixtures/Mounter.svelte diff --git a/src/__tests__/fixtures/Transitioner.svelte b/tests/fixtures/Transitioner.svelte similarity index 100% rename from src/__tests__/fixtures/Transitioner.svelte rename to tests/fixtures/Transitioner.svelte diff --git a/src/__tests__/fixtures/Typed.svelte b/tests/fixtures/Typed.svelte similarity index 100% rename from src/__tests__/fixtures/Typed.svelte rename to tests/fixtures/Typed.svelte diff --git a/src/__tests__/fixtures/TypedRunes.svelte b/tests/fixtures/TypedRunes.svelte similarity index 100% rename from src/__tests__/fixtures/TypedRunes.svelte rename to tests/fixtures/TypedRunes.svelte diff --git a/src/__tests__/mount.test.js b/tests/mount.test.js similarity index 100% rename from src/__tests__/mount.test.js rename to tests/mount.test.js diff --git a/src/__tests__/multi-base.test.js b/tests/multi-base.test.js similarity index 100% rename from src/__tests__/multi-base.test.js rename to tests/multi-base.test.js diff --git a/src/__tests__/render-runes.test-d.ts b/tests/render-runes.test-d.ts similarity index 97% rename from src/__tests__/render-runes.test-d.ts rename to tests/render-runes.test-d.ts index 51b7cfa..1e42544 100644 --- a/src/__tests__/render-runes.test-d.ts +++ b/tests/render-runes.test-d.ts @@ -1,7 +1,7 @@ +import * as subject from '@testing-library/svelte' import { expectTypeOf } from 'expect-type' import { describe, test, vi } from 'vitest' -import * as subject from '../index.js' import LegacyComponent from './fixtures/Typed.svelte' import Component from './fixtures/TypedRunes.svelte' diff --git a/src/__tests__/render-utilities.test-d.ts b/tests/render-utilities.test-d.ts similarity index 97% rename from src/__tests__/render-utilities.test-d.ts rename to tests/render-utilities.test-d.ts index d1e3869..c72f761 100644 --- a/src/__tests__/render-utilities.test-d.ts +++ b/tests/render-utilities.test-d.ts @@ -1,7 +1,7 @@ +import * as subject from '@testing-library/svelte' import { expectTypeOf } from 'expect-type' import { describe, test } from 'vitest' -import * as subject from '../index.js' import Component from './fixtures/Comp.svelte' describe('render query and utility types', () => { diff --git a/src/__tests__/render.test-d.ts b/tests/render.test-d.ts similarity index 97% rename from src/__tests__/render.test-d.ts rename to tests/render.test-d.ts index bf0a611..eb1c639 100644 --- a/src/__tests__/render.test-d.ts +++ b/tests/render.test-d.ts @@ -1,8 +1,8 @@ +import * as subject from '@testing-library/svelte' import { expectTypeOf } from 'expect-type' import { ComponentProps } from 'svelte' import { describe, test } from 'vitest' -import * as subject from '../index.js' import Component from './fixtures/Typed.svelte' describe('types', () => { diff --git a/src/__tests__/render.test.js b/tests/render.test.js similarity index 98% rename from src/__tests__/render.test.js rename to tests/render.test.js index f396751..943c920 100644 --- a/src/__tests__/render.test.js +++ b/tests/render.test.js @@ -1,7 +1,7 @@ import { render } from '@testing-library/svelte' import { beforeAll, describe, expect, test } from 'vitest' -import { COMPONENT_FIXTURES } from './utils.js' +import { COMPONENT_FIXTURES } from './_env.js' describe.each(COMPONENT_FIXTURES)('render ($mode)', ({ component }) => { const props = { name: 'World' } diff --git a/src/__tests__/rerender.test.js b/tests/rerender.test.js similarity index 95% rename from src/__tests__/rerender.test.js rename to tests/rerender.test.js index 52acd0f..2a20143 100644 --- a/src/__tests__/rerender.test.js +++ b/tests/rerender.test.js @@ -1,7 +1,7 @@ import { act, render, screen } from '@testing-library/svelte' import { beforeAll, describe, expect, test, vi } from 'vitest' -import { COMPONENT_FIXTURES, IS_SVELTE_5, MODE_RUNES } from './utils.js' +import { COMPONENT_FIXTURES, IS_SVELTE_5, MODE_RUNES } from './_env.js' describe.each(COMPONENT_FIXTURES)('rerender ($mode)', ({ mode, component }) => { let Comp diff --git a/src/__tests__/transition.test.js b/tests/transition.test.js similarity index 94% rename from src/__tests__/transition.test.js rename to tests/transition.test.js index a9c8e02..3acf1d0 100644 --- a/src/__tests__/transition.test.js +++ b/tests/transition.test.js @@ -2,8 +2,8 @@ import { render, screen, waitFor } from '@testing-library/svelte' import { userEvent } from '@testing-library/user-event' import { beforeEach, describe, expect, test, vi } from 'vitest' +import { IS_JSDOM, IS_SVELTE_5 } from './_env.js' import Transitioner from './fixtures/Transitioner.svelte' -import { IS_JSDOM, IS_SVELTE_5 } from './utils.js' describe.skipIf(IS_SVELTE_5)('transitions', () => { beforeEach(() => { diff --git a/src/__tests__/vite-plugin.test.js b/tests/vite-plugin.test.js similarity index 98% rename from src/__tests__/vite-plugin.test.js rename to tests/vite-plugin.test.js index 9232f65..92fc8f5 100644 --- a/src/__tests__/vite-plugin.test.js +++ b/tests/vite-plugin.test.js @@ -1,7 +1,7 @@ +import { svelteTesting } from '@testing-library/svelte/vite' import { beforeEach, describe, expect, test, vi } from 'vitest' -import { svelteTesting } from '../vite.js' -import { IS_JEST } from './utils.js' +import { IS_JEST } from './_env.js' describe.skipIf(IS_JEST)('vite plugin', () => { beforeEach(() => { diff --git a/tsconfig.build.json b/tsconfig.build.json index 0baa218..bfc566b 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -8,5 +8,5 @@ "rootDir": "src", "outDir": "types" }, - "exclude": ["src/**/__tests__/**"] + "include": ["src"] } diff --git a/tsconfig.json b/tsconfig.json index 0cc0fdf..fda8aba 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,11 @@ "skipLibCheck": true, "strict": true, "types": ["svelte", "vite/client", "vitest", "vitest/globals"], + "baseUrl": "./", + "paths": { + "@testing-library/svelte": ["./src"] + }, "plugins": [{ "name": "typescript-svelte-plugin" }] }, - "include": ["src"] + "include": ["src", "tests"] } diff --git a/tsconfig.legacy.json b/tsconfig.legacy.json index b5d9e57..304d872 100644 --- a/tsconfig.legacy.json +++ b/tsconfig.legacy.json @@ -1,8 +1,8 @@ { "extends": ["./tsconfig.json"], "exclude": [ - "src/__tests__/render-runes.test-d.ts", - "src/__tests__/fixtures/CompRunes.svelte", - "src/__tests__/fixtures/TypedRunes.svelte" + "tests/render-runes.test-d.ts", + "tests/fixtures/CompRunes.svelte", + "tests/fixtures/TypedRunes.svelte" ] } diff --git a/vite.config.js b/vite.config.js index 2db8a40..75bd66a 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,10 +1,9 @@ import { createRequire } from 'node:module' import { svelte } from '@sveltejs/vite-plugin-svelte' +import { svelteTesting } from '@testing-library/svelte/vite' import { defineConfig } from 'vite' -import { svelteTesting } from './src/vite.js' - const require = createRequire(import.meta.url) // https://vitejs.dev/config/ @@ -12,17 +11,17 @@ export default defineConfig({ plugins: [svelte(), svelteTesting()], test: { environment: 'jsdom', - setupFiles: ['./src/__tests__/_vitest-setup.js'], + setupFiles: ['./tests/_vitest-setup.js'], mockReset: true, unstubGlobals: true, unstubEnvs: true, coverage: { provider: 'v8', include: ['src/**/*'], - exclude: ['**/__tests__/**', 'src/vite.js', 'src/vitest.js'], }, alias: { - '@testing-library/svelte': require.resolve('.'), + '@testing-library/svelte/vite': require.resolve('./src/vite.js'), + '@testing-library/svelte': require.resolve('./src/index.js'), }, }, }) From 6096f05e805cf55474f52f303562f4013785d25f Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Wed, 7 May 2025 12:52:03 -0400 Subject: [PATCH 35/37] ci: expand matrix to include Node 22 (#435) --- .github/workflows/release.yml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 79dbe5a..c61b1d6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: main: # ignore all-contributors PRs if: ${{ !contains(github.head_ref, 'all-contributors') }} - name: Node ${{ matrix.node }}, Svelte ${{ matrix.svelte }}, ${{ matrix.check }} + name: Svelte ${{ matrix.svelte }}, Node ${{ matrix.node }}, ${{ matrix.check }} runs-on: ubuntu-latest # enable OIDC for codecov uploads @@ -27,20 +27,22 @@ jobs: strategy: fail-fast: false matrix: - node: ['16', '18', '20'] - svelte: ['3', '4'] + node: ['16', '18', '20', '22'] + svelte: ['3', '4', '5'] check: ['test:vitest:jsdom', 'test:vitest:happy-dom', 'test:jest'] + exclude: + # Don't run Svelte 3 on Node versions greater than 20 + - { svelte: '3', node: '22' } + # Only run Svelte 5 on Node versions greater than or equal to 20 + - { svelte: '5', node: '16' } + - { svelte: '5', node: '18' } include: # We only need to lint once, so do it on latest Node and Svelte - - { node: '20', svelte: '4', check: 'lint' } - # Run type checks in latest node - - { node: '20', svelte: '3', check: 'types:legacy' } - - { node: '20', svelte: '4', check: 'types:legacy' } - - { node: '20', svelte: '5', check: 'types' } - # Only run Svelte 5 checks on latest Node - - { node: '20', svelte: '5', check: 'test:vitest:jsdom' } - - { node: '20', svelte: '5', check: 'test:vitest:happy-dom' } - - { node: '20', svelte: '5', check: 'test:jest' } + - { svelte: '5', node: '22', check: 'lint' } + # Run type checks in latest applicable Node + - { svelte: '3', node: '20', check: 'types:legacy' } + - { svelte: '4', node: '22', check: 'types:legacy' } + - { svelte: '5', node: '22', check: 'types' } steps: - name: ⬇️ Checkout repo From f1fb8ddd7e1b599244e9359bb71db47268139f64 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Fri, 9 May 2025 12:11:54 -0400 Subject: [PATCH 36/37] ci: use specific dependencies for different Svelte/Node versions (#436) --- .github/workflows/release.yml | 6 ++-- .npmrc | 1 + CONTRIBUTING.md | 20 ++++++------ package.json | 41 ++++++++++-------------- scripts/changed-files | 3 -- scripts/install-dependencies | 44 ++++++++++++++------------ src/component-types.d.ts | 2 +- tests/envs/svelte3/node16/package.json | 24 ++++++++++++++ tests/envs/svelte3/package.json | 24 ++++++++++++++ tests/envs/svelte4/node16/package.json | 24 ++++++++++++++ tests/envs/svelte4/package.json | 24 ++++++++++++++ vite.config.js | 3 +- 12 files changed, 153 insertions(+), 63 deletions(-) delete mode 100755 scripts/changed-files create mode 100644 tests/envs/svelte3/node16/package.json create mode 100644 tests/envs/svelte3/package.json create mode 100644 tests/envs/svelte4/node16/package.json create mode 100644 tests/envs/svelte4/package.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c61b1d6..be02b70 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -75,10 +75,10 @@ jobs: - name: ⎔ Setup node uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: 📥 Download deps - run: npm install --no-package-lock + run: npm install - name: 🏗️ Build types run: npm run build @@ -102,7 +102,7 @@ jobs: - name: ⎔ Setup node uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: 📥 Downloads types build uses: actions/download-artifact@v4 diff --git a/.npmrc b/.npmrc index 43c97e7..86916fa 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ package-lock=false +engine-strict=true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c40705..41193a3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,11 +28,10 @@ npm run preview-release ## Development setup -After cloning the repository, install the project's dependencies and run the `validate` script to run all checks and tests to verify your setup. +After cloning the repository, use the `setup` script to install dependencies and run all checks: ```shell -npm install # or `pnpm install`, or `yarn install`, etc. -npm run validate +npm run setup ``` ### Lint and format @@ -40,13 +39,13 @@ npm run validate Run auto-formatting to ensure any changes adhere to the code style of the repository: ```shell -npm run format:delta +npm run format ``` To run lint and format checks without making any changes: ```shell -npm run lint:delta +npm run lint ``` ### Test @@ -63,18 +62,19 @@ npm run test:watch Use the provided script to set up your environment for different versions of Svelte: ```shell -# install Svelte 5 +# Svelte 5 npm run install:5 +npm run all -# install Svelte 4 +# Svelte 4 npm run install:4 +npm run all:legacy -# install Svelte 3 +# Svelte 3 npm run install:3 +npm run all:legacy ``` -Not all checks will pass on `svelte<5`. Reference the CI workflows to see which checks are expected to pass on older versions. - ### Docs Use the `toc` script to ensure the README's table of contents is up to date: diff --git a/package.json b/package.json index d686b2c..7e50de7 100644 --- a/package.json +++ b/package.json @@ -53,16 +53,11 @@ ], "scripts": { "all": "npm-run-all contributors:generate toc format types build test:vitest:* test:jest", + "all:legacy": "npm-run-all types:legacy test:vitest:* test:jest", "toc": "doctoc README.md", "lint": "prettier . --check && eslint .", - "lint:delta": "npm-run-all -p prettier:delta eslint:delta", - "prettier:delta": "prettier --check `./scripts/changed-files`", - "eslint:delta": "eslint `./scripts/changed-files`", "format": "prettier . --write && eslint . --fix", - "format:delta": "npm-run-all format:prettier:delta format:eslint:delta", - "format:prettier:delta": "prettier --write `./scripts/changed-files`", - "format:eslint:delta": "eslint --fix `./scripts/changed-files`", - "setup": "npm install && npm run all", + "setup": "npm run install:5 && npm run all", "test": "vitest run --coverage", "test:watch": "vitest", "test:vitest:jsdom": "vitest run --coverage --environment jsdom", @@ -70,7 +65,6 @@ "test:jest": "npx --node-options=\"--experimental-vm-modules --no-warnings\" jest --coverage", "types": "svelte-check", "types:legacy": "svelte-check --tsconfig tsconfig.legacy.json", - "validate": "npm-run-all test:vitest:* test:jest types build", "build": "tsc -p tsconfig.build.json && cp src/component-types.d.ts types", "contributors:add": "all-contributors add", "contributors:generate": "all-contributors generate", @@ -93,19 +87,18 @@ } }, "dependencies": { - "@testing-library/dom": "^10.0.0" + "@testing-library/dom": "9.x.x || 10.x.x" }, "devDependencies": { "@jest/globals": "^29.7.0", - "@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0 || ^4.0.0", - "@testing-library/jest-dom": "^6.3.0", - "@testing-library/user-event": "^14.5.2", + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/user-event": "^14.6.1", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", - "@vitest/coverage-v8": "0.x.x || ^1.0.0 || ^2.0.2", + "@vitest/coverage-v8": "^3.1.3", "all-contributors-cli": "^6.26.1", "doctoc": "^2.2.1", - "esbuild": "*", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-config-standard": "^17.1.0", @@ -114,20 +107,20 @@ "eslint-plugin-promise": "^6.4.0", "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-svelte": "^2.42.0", - "expect-type": "^1.1.0", - "happy-dom": "^16.3.0", + "expect-type": "^1.2.1", + "happy-dom": "^17.4.6", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "jsdom": "^25.0.0", + "jsdom": "^26.1.0", "npm-run-all": "^4.1.5", - "prettier": "^3.3.3", - "prettier-plugin-svelte": "^3.2.5", - "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", - "svelte-check": "^3.0.0 || ^4.0.4", + "prettier": "^3.5.3", + "prettier-plugin-svelte": "^3.3.3", + "svelte": "^5.28.2", + "svelte-check": "^4.1.7", "svelte-jester": "^5.0.0", - "typescript": "^5.5.3", + "typescript": "^5.8.3", "typescript-svelte-plugin": "^0.3.46", - "vite": "^4.0.0 || ^5.3.3", - "vitest": "0.x.x || ^1.0.0 || ^2.0.2" + "vite": "^6.3.5", + "vitest": "^3.1.3" } } diff --git a/scripts/changed-files b/scripts/changed-files deleted file mode 100755 index 1275bd8..0000000 --- a/scripts/changed-files +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -git diff --name-only --diff-filter=d main diff --git a/scripts/install-dependencies b/scripts/install-dependencies index 8f403a0..aef6257 100755 --- a/scripts/install-dependencies +++ b/scripts/install-dependencies @@ -2,30 +2,34 @@ # Install dependencies for a given version of Svelte set -euxo pipefail -svelte_version=${1} -node_version=$(node --version) +svelte_version="${1-}" +node_version=$(node --version | sed 's/^v\([0-9]*\).*/\1/') +env_dir="tests/envs/svelte$svelte_version" +env_dir_by_node="$env_dir/node$node_version" -rm -rf node_modules -npm install --no-package-lock - -if [[ "${svelte_version}" == "3" ]]; then - npm uninstall --no-save vite vitest @vitest/coverage-v8 @sveltejs/vite-plugin-svelte svelte-check svelte - npm install --no-save vite@4 vitest@0.x.x @vitest/coverage-v8@0.x.x @sveltejs/vite-plugin-svelte@2 svelte-check@3 svelte@3 -elif [[ "${svelte_version}" == "4" ]]; then - npm uninstall --no-save @sveltejs/vite-plugin-svelte svelte - npm install --no-save @sveltejs/vite-plugin-svelte@3 svelte@4 +if [[ -d $env_dir_by_node ]]; then + env_dir="$env_dir_by_node" fi -if [[ "${node_version}" =~ "v16" ]]; then - npm uninstall --no-save vite vitest @vitest/coverage-v8 @sveltejs/vite-plugin-svelte - npm install --no-save vite@4 vitest@0.x.x @vitest/coverage-v8@0.x.x @sveltejs/vite-plugin-svelte@2 +if [[ "$svelte_version" == "5" ]]; then + rm -rf coverage node_modules + npm install + exit 0 fi -npm dedupe -installed_version=$(npm ls --depth=0 --parseable svelte@${svelte_version}) - -if [[ -z "${installed_version}" ]]; then - echo "Error: expected svelte@${svelte_version}" - npm ls --depth=0 svelte +if [[ -z "$svelte_version" ]]; then + echo "Invalid usage: missing Svelte version" >&2; exit 1 fi + +if [[ ! -d "$env_dir" ]]; then + echo "Error: package.json for Svelte $svelte_version, Node $node_version not found" 1>&2 + exit 2 +fi + +rm -rf coverage node_modules "$env_dir/node_modules" +pushd "$env_dir" +npm install --no-package-lock --engine-strict +npm ls "$env_dir" svelte +popd +mv "$env_dir/node_modules" . diff --git a/src/component-types.d.ts b/src/component-types.d.ts index 8300243..007f1e4 100644 --- a/src/component-types.d.ts +++ b/src/component-types.d.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-redundant-type-constituents */ +/* eslint-disable @typescript-eslint/no-explicit-any */ import type { Component as ModernComponent, ComponentConstructorOptions as LegacyConstructorOptions, diff --git a/tests/envs/svelte3/node16/package.json b/tests/envs/svelte3/node16/package.json new file mode 100644 index 0000000..8acb921 --- /dev/null +++ b/tests/envs/svelte3/node16/package.json @@ -0,0 +1,24 @@ +{ + "private": true, + "engines": { + "node": "16.x.x" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "2.x.x", + "@testing-library/dom": "9.x.x", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/user-event": "^14.6.1", + "@vitest/coverage-v8": "0.x.x", + "expect-type": "^1.2.1", + "happy-dom": "14.x.x", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jsdom": "22.x.x", + "npm-run-all": "^4.1.5", + "svelte": "3.x.x", + "svelte-check": "3.x.x", + "svelte-jester": "3.x.x", + "vite": "4.x.x", + "vitest": "0.x.x" + } +} diff --git a/tests/envs/svelte3/package.json b/tests/envs/svelte3/package.json new file mode 100644 index 0000000..d563d97 --- /dev/null +++ b/tests/envs/svelte3/package.json @@ -0,0 +1,24 @@ +{ + "private": true, + "engines": { + "node": ">=18" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "2.x.x", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/user-event": "^14.6.1", + "@vitest/coverage-v8": "0.x.x", + "expect-type": "^1.2.1", + "happy-dom": "^17.4.6", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jsdom": "^26.1.0", + "npm-run-all": "^4.1.5", + "svelte": "3.x.x", + "svelte-check": "3.x.x", + "svelte-jester": "3.x.x", + "vite": "4.x.x", + "vitest": "0.x.x" + } +} diff --git a/tests/envs/svelte4/node16/package.json b/tests/envs/svelte4/node16/package.json new file mode 100644 index 0000000..ce420a1 --- /dev/null +++ b/tests/envs/svelte4/node16/package.json @@ -0,0 +1,24 @@ +{ + "private": true, + "engines": { + "node": "16.x.x" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "2.x.x", + "@testing-library/dom": "9.x.x", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/user-event": "^14.6.1", + "@vitest/coverage-v8": "0.x.x", + "expect-type": "^1.2.1", + "happy-dom": "14.x.x", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jsdom": "22.x.x", + "npm-run-all": "^4.1.5", + "svelte": "4.x.x", + "svelte-check": "3.x.x", + "svelte-jester": "3.x.x", + "vite": "4.x.x", + "vitest": "0.x.x" + } +} diff --git a/tests/envs/svelte4/package.json b/tests/envs/svelte4/package.json new file mode 100644 index 0000000..b90351d --- /dev/null +++ b/tests/envs/svelte4/package.json @@ -0,0 +1,24 @@ +{ + "private": true, + "engines": { + "node": ">=18" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "3.x.x", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/user-event": "^14.6.1", + "@vitest/coverage-v8": "2.x.x", + "expect-type": "^1.2.1", + "happy-dom": "^17.4.6", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jsdom": "^26.1.0", + "npm-run-all": "^4.1.5", + "svelte": "4.x.x", + "svelte-check": "^4.1.7", + "svelte-jester": "^5.0.0", + "vite": "5.x.x", + "vitest": "2.x.x" + } +} diff --git a/vite.config.js b/vite.config.js index 75bd66a..65e1ca7 100644 --- a/vite.config.js +++ b/vite.config.js @@ -6,9 +6,8 @@ import { defineConfig } from 'vite' const require = createRequire(import.meta.url) -// https://vitejs.dev/config/ export default defineConfig({ - plugins: [svelte(), svelteTesting()], + plugins: [svelte({ hot: false }), svelteTesting()], test: { environment: 'jsdom', setupFiles: ['./tests/_vitest-setup.js'], From 1aa370137003ce8e887d96f881d52c33d3dd6f12 Mon Sep 17 00:00:00 2001 From: Michael Cousins Date: Fri, 9 May 2025 12:21:42 -0400 Subject: [PATCH 37/37] style: update to ESLint v9 and rework lint config (#438) --- .eslintignore | 6 -- .eslintrc.cjs | 45 --------------- .github/dependabot.yml | 9 --- .prettierignore | 3 - .prettierrc.yaml | 9 --- eslint.config.js | 95 ++++++++++++++++++++++++++++++++ jest.config.js | 6 +- package.json | 20 ++++--- prettier.config.js | 14 +++++ src/core/index.js | 14 +---- src/index.js | 1 - src/pure.js | 17 ++++-- src/vite.js | 12 ++-- tests/_env.js | 2 +- tests/act.test.js | 10 ++-- tests/context.test.js | 6 +- tests/events.test.js | 10 ++-- tests/fixtures/Typed.svelte | 2 +- tests/fixtures/TypedRunes.svelte | 2 +- tests/render.test.js | 27 ++++----- tests/rerender.test.js | 4 +- tests/transition.test.js | 13 +++-- 22 files changed, 182 insertions(+), 145 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.cjs delete mode 100644 .prettierrc.yaml create mode 100644 eslint.config.js create mode 100644 prettier.config.js diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 39f9d9e..0000000 --- a/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -scripts/* -.eslintignore -.prettierignore -.github/workflows/* -*.md -types diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 77f8b7c..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,45 +0,0 @@ -module.exports = { - root: true, - env: { - browser: true, - es6: true, - }, - extends: ['standard', 'plugin:svelte/recommended', 'prettier'], - plugins: ['svelte', 'simple-import-sort'], - rules: { - 'simple-import-sort/imports': 'error', - 'simple-import-sort/exports': 'error', - }, - overrides: [ - { - files: ['*.svelte'], - parser: 'svelte-eslint-parser', - parserOptions: { - parser: '@typescript-eslint/parser', - }, - rules: { - 'no-undef-init': 'off', - 'prefer-const': 'off', - 'svelte/no-unused-svelte-ignore': 'off', - }, - }, - { - files: ['*.ts'], - parser: '@typescript-eslint/parser', - parserOptions: { - project: './tsconfig.json', - }, - extends: [ - 'plugin:@typescript-eslint/strict-type-checked', - 'plugin:@typescript-eslint/stylistic-type-checked', - 'prettier', - ], - }, - ], - parserOptions: { - ecmaVersion: 2022, - sourceType: 'module', - }, - globals: { afterEach: 'readonly', $state: 'readonly', $props: 'readonly' }, - ignorePatterns: ['!/.*'], -} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c9eb8a7..434a8bb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -23,15 +23,6 @@ updates: development: dependency-type: 'development' - # TODO(mcous, 2024-04-30): update to ESLint v9 + flat config - ignore: - - dependency-name: 'eslint' - versions: ['>=9'] - - dependency-name: 'eslint-plugin-n' - versions: ['>=17'] - - dependency-name: 'eslint-plugin-promise' - versions: ['>=7'] - # Update GitHub Actions dependencies - package-ecosystem: 'github-actions' directory: '/' diff --git a/.prettierignore b/.prettierignore index 5e50c20..c750ecf 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1 @@ -scripts/* -.eslintignore -.prettierignore .all-contributorsrc diff --git a/.prettierrc.yaml b/.prettierrc.yaml deleted file mode 100644 index 0a2ace3..0000000 --- a/.prettierrc.yaml +++ /dev/null @@ -1,9 +0,0 @@ -semi: false -singleQuote: true -trailingComma: es5 -plugins: - - prettier-plugin-svelte -overrides: - - files: '*.svelte' - options: - parser: svelte diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..c03de8c --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,95 @@ +import js from '@eslint/js' +import eslintPluginVitest from '@vitest/eslint-plugin' +import eslintConfigPrettier from 'eslint-config-prettier' +import eslintPluginJestDom from 'eslint-plugin-jest-dom' +import eslintPluginPromise from 'eslint-plugin-promise' +import eslintPluginSimpleImportSort from 'eslint-plugin-simple-import-sort' +import eslintPluginSvelte from 'eslint-plugin-svelte' +import eslintPluginTestingLibrary from 'eslint-plugin-testing-library' +import eslintPluginUnicorn from 'eslint-plugin-unicorn' +import globals from 'globals' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + js.configs.recommended, + tseslint.configs.strict, + tseslint.configs.stylistic, + eslintPluginUnicorn.configs['flat/recommended'], + eslintPluginPromise.configs['flat/recommended'], + eslintPluginSvelte.configs['flat/recommended'], + eslintPluginSvelte.configs['flat/prettier'], + eslintConfigPrettier, + { + name: 'settings', + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + parserOptions: { + parser: tseslint.parser, + extraFileExtensions: ['.svelte'], + }, + globals: { + ...globals.browser, + ...globals.node, + ...globals.jest, + }, + }, + }, + { + name: 'ignores', + ignores: ['coverage', 'types'], + }, + { + name: 'simple-import-sort', + plugins: { + 'simple-import-sort': eslintPluginSimpleImportSort, + }, + rules: { + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', + }, + }, + { + name: 'tests', + files: ['**/*.test.js'], + extends: [ + eslintPluginVitest.configs.recommended, + eslintPluginJestDom.configs['flat/recommended'], + eslintPluginTestingLibrary.configs['flat/dom'], + ], + rules: { + 'testing-library/no-node-access': [ + 'error', + { allowContainerFirstChild: true }, + ], + }, + }, + { + name: 'extras', + rules: { + 'unicorn/prevent-abbreviations': 'off', + }, + }, + { + name: 'svelte-extras', + files: ['**/*.svelte'], + rules: { + 'svelte/no-unused-svelte-ignore': 'off', + 'unicorn/filename-case': ['error', { case: 'pascalCase' }], + 'unicorn/no-useless-undefined': 'off', + }, + }, + { + name: 'ts-extras', + files: ['**/*.ts'], + extends: [ + tseslint.configs.strictTypeChecked, + tseslint.configs.stylisticTypeChecked, + ], + languageOptions: { + parserOptions: { + projectService: true, + }, + }, + } +) diff --git a/jest.config.js b/jest.config.js index ff518c4..b590229 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,9 @@ import { VERSION as SVELTE_VERSION } from 'svelte/compiler' const SVELTE_TRANSFORM_PATTERN = - SVELTE_VERSION >= '5' ? '^.+\\.svelte(?:\\.js)?$' : '^.+\\.svelte$' + SVELTE_VERSION >= '5' + ? String.raw`^.+\.svelte(?:\.js)?$` + : String.raw`^.+\.svelte$` export default { testMatch: ['/tests/**/*.test.js'], @@ -15,7 +17,7 @@ export default { injectGlobals: false, moduleNameMapper: { '^vitest$': '/tests/_jest-vitest-alias.js', - '^@testing-library\\/svelte$': '/src/index.js', + [String.raw`^@testing-library\/svelte$`]: '/src/index.js', }, resetMocks: true, restoreMocks: true, diff --git a/package.json b/package.json index 7e50de7..fea66a7 100644 --- a/package.json +++ b/package.json @@ -90,24 +90,25 @@ "@testing-library/dom": "9.x.x || 10.x.x" }, "devDependencies": { + "@eslint/js": "^9.26.0", "@jest/globals": "^29.7.0", "@sveltejs/vite-plugin-svelte": "^5.0.3", "@testing-library/jest-dom": "^6.6.3", "@testing-library/user-event": "^14.6.1", - "@typescript-eslint/eslint-plugin": "^8.0.0", - "@typescript-eslint/parser": "^8.0.0", "@vitest/coverage-v8": "^3.1.3", + "@vitest/eslint-plugin": "^1.1.44", "all-contributors-cli": "^6.26.1", "doctoc": "^2.2.1", - "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-n": "^16.6.2", - "eslint-plugin-promise": "^6.4.0", + "eslint": "^9.26.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-jest-dom": "^5.5.0", + "eslint-plugin-promise": "^7.2.1", "eslint-plugin-simple-import-sort": "^12.1.1", - "eslint-plugin-svelte": "^2.42.0", + "eslint-plugin-svelte": "^3.5.1", + "eslint-plugin-testing-library": "^7.1.1", + "eslint-plugin-unicorn": "^59.0.1", "expect-type": "^1.2.1", + "globals": "^16.1.0", "happy-dom": "^17.4.6", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -119,6 +120,7 @@ "svelte-check": "^4.1.7", "svelte-jester": "^5.0.0", "typescript": "^5.8.3", + "typescript-eslint": "^8.32.0", "typescript-svelte-plugin": "^0.3.46", "vite": "^6.3.5", "vitest": "^3.1.3" diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..8c5a52d --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,14 @@ +export default { + semi: false, + singleQuote: true, + trailingComma: 'es5', + plugins: ['prettier-plugin-svelte'], + overrides: [ + { + files: '*.svelte', + options: { + parser: 'svelte', + }, + }, + ], +} diff --git a/src/core/index.js b/src/core/index.js index f4a40aa..9e41adf 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -7,10 +7,7 @@ */ import * as LegacyCore from './legacy.js' import * as ModernCore from './modern.svelte.js' -import { - createValidateOptions, - UnknownSvelteOptionsError, -} from './validate-options.js' +import { createValidateOptions } from './validate-options.js' const { mount, unmount, updateProps, allowedOptions } = ModernCore.IS_MODERN_SVELTE ? ModernCore : LegacyCore @@ -18,10 +15,5 @@ const { mount, unmount, updateProps, allowedOptions } = /** Validate component options. */ const validateOptions = createValidateOptions(allowedOptions) -export { - mount, - UnknownSvelteOptionsError, - unmount, - updateProps, - validateOptions, -} +export { mount, unmount, updateProps, validateOptions } +export { UnknownSvelteOptionsError } from './validate-options.js' diff --git a/src/index.js b/src/index.js index 2704824..5a65c08 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,3 @@ -/* eslint-disable import/export */ import { act, cleanup } from './pure.js' // If we're running in a test runner that supports afterEach diff --git a/src/pure.js b/src/pure.js index 875f87c..44e68d6 100644 --- a/src/pure.js +++ b/src/pure.js @@ -65,6 +65,7 @@ const render = (Component, options = {}, renderOptions = {}) => { const queries = getQueriesForElement(baseElement, renderOptions.queries) const target = + // eslint-disable-next-line unicorn/prefer-dom-node-append options.target ?? baseElement.appendChild(document.createElement('div')) targetCache.add(target) @@ -116,14 +117,18 @@ const cleanupTarget = (target) => { const inCache = targetCache.delete(target) if (inCache && target.parentNode === document.body) { - document.body.removeChild(target) + target.remove() } } /** Unmount all components and remove elements added to ``. */ const cleanup = () => { - componentCache.forEach(cleanupComponent) - targetCache.forEach(cleanupTarget) + for (const component of componentCache) { + cleanupComponent(component) + } + for (const target of targetCache) { + cleanupTarget(target) + } } /** @@ -163,12 +168,12 @@ const fireEvent = async (...args) => { return event } -Object.keys(baseFireEvent).forEach((key) => { +for (const [key, baseEvent] of Object.entries(baseFireEvent)) { fireEvent[key] = async (...args) => { - const event = baseFireEvent[key](...args) + const event = baseEvent(...args) await tick() return event } -}) +} export { act, cleanup, fireEvent, render } diff --git a/src/vite.js b/src/vite.js index 1ad712f..b57c886 100644 --- a/src/vite.js +++ b/src/vite.js @@ -1,5 +1,5 @@ -import { dirname, join } from 'node:path' -import { fileURLToPath } from 'node:url' +import path from 'node:path' +import url from 'node:url' /** * Vite plugin to configure @testing-library/svelte. @@ -50,8 +50,8 @@ const addBrowserCondition = (config) => { const browserConditionIndex = conditions.indexOf('browser') if ( - nodeConditionIndex >= 0 && - (nodeConditionIndex < browserConditionIndex || browserConditionIndex < 0) + nodeConditionIndex !== -1 && + (nodeConditionIndex < browserConditionIndex || browserConditionIndex === -1) ) { conditions.splice(nodeConditionIndex, 0, 'browser') } @@ -77,7 +77,9 @@ const addAutoCleanup = (config) => { setupFiles = [setupFiles] } - setupFiles.push(join(dirname(fileURLToPath(import.meta.url)), './vitest.js')) + setupFiles.push( + path.join(path.dirname(url.fileURLToPath(import.meta.url)), './vitest.js') + ) test.setupFiles = setupFiles config.test = test diff --git a/tests/_env.js b/tests/_env.js index f637d6f..96b30f4 100644 --- a/tests/_env.js +++ b/tests/_env.js @@ -1,6 +1,6 @@ import { VERSION as SVELTE_VERSION } from 'svelte/compiler' -export const IS_JSDOM = window.navigator.userAgent.includes('jsdom') +export const IS_JSDOM = globalThis.navigator.userAgent.includes('jsdom') export const IS_HAPPYDOM = !IS_JSDOM // right now it's happy or js diff --git a/tests/act.test.js b/tests/act.test.js index 75c9ded..9308d75 100644 --- a/tests/act.test.js +++ b/tests/act.test.js @@ -1,14 +1,14 @@ import { setTimeout } from 'node:timers/promises' -import { act, render } from '@testing-library/svelte' +import { act, render, screen } from '@testing-library/svelte' import { describe, expect, test } from 'vitest' import Comp from './fixtures/Comp.svelte' describe('act', () => { test('state updates are flushed', async () => { - const { getByText } = render(Comp) - const button = getByText('Button') + render(Comp) + const button = screen.getByText('Button') expect(button).toHaveTextContent('Button') @@ -20,8 +20,8 @@ describe('act', () => { }) test('accepts async functions', async () => { - const { getByText } = render(Comp) - const button = getByText('Button') + render(Comp) + const button = screen.getByText('Button') await act(async () => { await setTimeout(100) diff --git a/tests/context.test.js b/tests/context.test.js index da54b9d..e9d83eb 100644 --- a/tests/context.test.js +++ b/tests/context.test.js @@ -1,4 +1,4 @@ -import { render } from '@testing-library/svelte' +import { render, screen } from '@testing-library/svelte' import { expect, test } from 'vitest' import Comp from './fixtures/Context.svelte' @@ -6,9 +6,9 @@ import Comp from './fixtures/Context.svelte' test('can set a context', () => { const message = 'Got it' - const { getByText } = render(Comp, { + render(Comp, { context: new Map(Object.entries({ foo: { message } })), }) - expect(getByText(message)).toBeTruthy() + expect(screen.getByText(message)).toBeInTheDocument() }) diff --git a/tests/events.test.js b/tests/events.test.js index e0aba4d..9864692 100644 --- a/tests/events.test.js +++ b/tests/events.test.js @@ -1,12 +1,12 @@ -import { fireEvent, render } from '@testing-library/svelte' +import { fireEvent, render, screen } from '@testing-library/svelte' import { describe, expect, test } from 'vitest' import Comp from './fixtures/Comp.svelte' describe('events', () => { test('state changes are flushed after firing an event', async () => { - const { getByText } = render(Comp, { props: { name: 'World' } }) - const button = getByText('Button') + render(Comp, { props: { name: 'World' } }) + const button = screen.getByText('Button') const result = fireEvent.click(button) @@ -15,8 +15,8 @@ describe('events', () => { }) test('calling `fireEvent` directly works too', async () => { - const { getByText } = render(Comp, { props: { name: 'World' } }) - const button = getByText('Button') + render(Comp, { props: { name: 'World' } }) + const button = screen.getByText('Button') const result = fireEvent( button, diff --git a/tests/fixtures/Typed.svelte b/tests/fixtures/Typed.svelte index 383214f..dad8e14 100644 --- a/tests/fixtures/Typed.svelte +++ b/tests/fixtures/Typed.svelte @@ -4,7 +4,7 @@ export let name: string export let count: number - export const hello: string = 'hello' + export const hello = 'hello' const dispatch = createEventDispatcher<{ greeting: string }>() diff --git a/tests/fixtures/TypedRunes.svelte b/tests/fixtures/TypedRunes.svelte index 979be41..0fb690b 100644 --- a/tests/fixtures/TypedRunes.svelte +++ b/tests/fixtures/TypedRunes.svelte @@ -1,7 +1,7 @@

hello {name}

diff --git a/tests/render.test.js b/tests/render.test.js index 943c920..19bf2c3 100644 --- a/tests/render.test.js +++ b/tests/render.test.js @@ -1,4 +1,4 @@ -import { render } from '@testing-library/svelte' +import { render, screen } from '@testing-library/svelte' import { beforeAll, describe, expect, test } from 'vitest' import { COMPONENT_FIXTURES } from './_env.js' @@ -12,14 +12,14 @@ describe.each(COMPONENT_FIXTURES)('render ($mode)', ({ component }) => { }) test('renders component into the document', () => { - const { getByText } = render(Comp, { props }) + render(Comp, { props }) - expect(getByText('Hello World!')).toBeInTheDocument() + expect(screen.getByText('Hello World!')).toBeInTheDocument() }) test('accepts props directly', () => { - const { getByText } = render(Comp, props) - expect(getByText('Hello World!')).toBeInTheDocument() + render(Comp, props) + expect(screen.getByText('Hello World!')).toBeInTheDocument() }) test('throws error when mixing svelte component options and props', () => { @@ -35,8 +35,8 @@ describe.each(COMPONENT_FIXTURES)('render ($mode)', ({ component }) => { }) test('should return a container object wrapping the DOM of the rendered component', () => { - const { container, getByTestId } = render(Comp, props) - const firstElement = getByTestId('test') + const { container } = render(Comp, props) + const firstElement = screen.getByTestId('test') expect(container.firstChild).toBe(firstElement) }) @@ -73,17 +73,14 @@ describe.each(COMPONENT_FIXTURES)('render ($mode)', ({ component }) => { const baseElement = document.body const target = document.createElement('section') const anchor = document.createElement('div') - baseElement.appendChild(target) - target.appendChild(anchor) + baseElement.append(target) + target.append(anchor) - const { getByTestId } = render( - Comp, - { props, target, anchor }, - { baseElement } - ) - const firstElement = getByTestId('test') + render(Comp, { props, target, anchor }, { baseElement }) + const firstElement = screen.getByTestId('test') expect(target.firstChild).toBe(firstElement) + // eslint-disable-next-line testing-library/no-node-access expect(target.lastChild).toBe(anchor) }) }) diff --git a/tests/rerender.test.js b/tests/rerender.test.js index 2a20143..2fdff0d 100644 --- a/tests/rerender.test.js +++ b/tests/rerender.test.js @@ -39,8 +39,8 @@ describe.each(COMPONENT_FIXTURES)('rerender ($mode)', ({ mode, component }) => { ? { name: 'World' } : { accessors: true, props: { name: 'World' } } - const { component, getByText } = render(Comp, componentOptions) - const element = getByText('Hello World!') + const { component } = render(Comp, componentOptions) + const element = screen.getByText('Hello World!') expect(element).toBeInTheDocument() expect(component.name).toBe('World') diff --git a/tests/transition.test.js b/tests/transition.test.js index 3acf1d0..27b236e 100644 --- a/tests/transition.test.js +++ b/tests/transition.test.js @@ -6,12 +6,13 @@ import { IS_JSDOM, IS_SVELTE_5 } from './_env.js' import Transitioner from './fixtures/Transitioner.svelte' describe.skipIf(IS_SVELTE_5)('transitions', () => { - beforeEach(() => { - if (!IS_JSDOM) return - - const raf = (fn) => setTimeout(() => fn(new Date()), 16) - vi.stubGlobal('requestAnimationFrame', raf) - }) + if (IS_JSDOM) { + beforeEach(() => { + vi.stubGlobal('requestAnimationFrame', (fn) => + setTimeout(() => fn(new Date()), 16) + ) + }) + } test('on:introend', async () => { const user = userEvent.setup()