Skip to content

Commit 5192455

Browse files
committed
refactor(types): build types from JS source
1 parent cb66333 commit 5192455

File tree

9 files changed

+165
-109
lines changed

9 files changed

+165
-109
lines changed

.github/workflows/release.yml

+30-1
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,35 @@ jobs:
5959
run: npm run test:${{ matrix.test-runner }}
6060

6161
- name: ▶️ Run type-checks
62-
if: ${{ matrix.node == '20' && matrix.svelte == '4' && matrix.test-runner == 'vitest:jsdom' }}
62+
if: ${{ matrix.node == '20' && matrix.svelte != '3' && matrix.test-runner == 'vitest:jsdom' }}
6363
run: npm run types
6464

6565
- name: ⬆️ Upload coverage report
6666
uses: codecov/codecov-action@v3
6767

68+
build:
69+
runs-on: ubuntu-latest
70+
steps:
71+
- name: ⬇️ Checkout repo
72+
uses: actions/checkout@v4
73+
74+
- name: ⎔ Setup node
75+
uses: actions/setup-node@v4
76+
with:
77+
node-version: 20
78+
79+
- name: 📥 Download deps
80+
run: npm install --no-package-lock
81+
82+
- name: 🏗️ Build types
83+
run: npm run build
84+
85+
- name: ⬆️ Upload types build
86+
uses: actions/upload-artifact@v4
87+
with:
88+
name: types
89+
path: types
90+
6891
release:
6992
needs: main
7093
runs-on: ubuntu-latest
@@ -83,6 +106,12 @@ jobs:
83106
- name: 📥 Download deps
84107
run: npm install --no-package-lock
85108

109+
- name: 📥 Downloads types build
110+
uses: actions/download-artifact@v4
111+
with:
112+
name: types
113+
path: types
114+
86115
- name: 🚀 Release
87116
uses: cycjimmy/semantic-release-action@v2
88117
with:

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ dist
99
yarn-error.log
1010
package-lock.json
1111
yarn.lock
12+
13+
# generated typing output
14+
types

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@
6969
"test:vitest:happy-dom": "vitest run --coverage --environment happy-dom",
7070
"test:jest": "npx --node-options=\"--experimental-vm-modules --no-warnings\" jest --coverage",
7171
"types": "svelte-check",
72-
"validate": "npm-run-all test:vitest:* types",
72+
"build": "tsc -p tsconfig.build.json",
73+
"validate": "npm-run-all test:vitest:* types build",
7374
"contributors:add": "all-contributors add",
7475
"contributors:generate": "all-contributors generate"
7576
},

types/types.test-d.ts renamed to src/__tests__/types.test-d.ts

+34-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { expectTypeOf } from 'expect-type'
22
import type { ComponentProps, SvelteComponent } from 'svelte'
33
import { describe, test } from 'vitest'
44

5-
import Simple from '../src/__tests__/fixtures/Simple.svelte'
6-
import * as subject from './index.js'
5+
import * as subject from '../index.js'
6+
import Simple from './fixtures/Simple.svelte'
77

88
describe('types', () => {
99
test('render is a function that accepts a Svelte component', () => {
@@ -62,4 +62,36 @@ describe('types', () => {
6262

6363
expectTypeOf(result.getByVibes).parameters.toMatchTypeOf<[vibes: string]>()
6464
})
65+
66+
test('act is an async function', () => {
67+
expectTypeOf(subject.act).toMatchTypeOf<() => Promise<void>>()
68+
})
69+
70+
test('act accepts a sync function', () => {
71+
expectTypeOf(subject.act).toMatchTypeOf<(fn: () => void) => Promise<void>>()
72+
})
73+
74+
test('act accepts an async function', () => {
75+
expectTypeOf(subject.act).toMatchTypeOf<
76+
(fn: () => Promise<void>) => Promise<void>
77+
>()
78+
})
79+
80+
test('fireEvent is an async function', () => {
81+
expectTypeOf(subject.fireEvent).toMatchTypeOf<
82+
(
83+
element: Element | Node | Document | Window,
84+
event: Event
85+
) => Promise<boolean>
86+
>()
87+
})
88+
89+
test('fireEvent[eventName] is an async function', () => {
90+
expectTypeOf(subject.fireEvent.click).toMatchTypeOf<
91+
(
92+
element: Element | Node | Document | Window,
93+
options?: {}
94+
) => Promise<boolean>
95+
>()
96+
})
6597
})

src/pure.js

+82-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,44 @@
1-
import {
2-
fireEvent as dtlFireEvent,
3-
getQueriesForElement,
4-
prettyDOM,
5-
} from '@testing-library/dom'
1+
import * as DOMTestingLibrary from '@testing-library/dom'
62
import * as Svelte from 'svelte'
73
import { VERSION as SVELTE_VERSION } from 'svelte/compiler'
84

95
const IS_SVELTE_5 = /^5\./.test(SVELTE_VERSION)
106

7+
/**
8+
* Customize how Svelte renders the component.
9+
*
10+
* @template {Svelte.SvelteComponent} C
11+
* @typedef {Svelte.ComponentProps<C> | Partial<Svelte.ComponentConstructorOptions<Svelte.ComponentProps<C>>>} SvelteComponentOptions
12+
*/
13+
14+
/**
15+
* Customize how Testing Library sets up the document and binds queries.
16+
*
17+
* @template {DOMTestingLibrary.Queries} [Q=typeof DOMTestingLibrary.queries]
18+
* @typedef {{
19+
* baseElement?: HTMLElement
20+
* queries?: Q
21+
* }} RenderOptions
22+
*/
23+
24+
/**
25+
* The rendered component and bound testing functions.
26+
*
27+
* @template {Svelte.SvelteComponent} C
28+
* @template {DOMTestingLibrary.Queries} [Q=typeof DOMTestingLibrary.queries]
29+
*
30+
* @typedef {{
31+
* container: HTMLElement
32+
* baseElement: HTMLElement
33+
* component: C
34+
* debug: (el?: HTMLElement | DocumentFragment) => void
35+
* rerender: (props: Partial<Svelte.ComponentProps<C>>) => Promise<void>
36+
* unmount: () => void
37+
* } & {
38+
* [P in keyof Q]: DOMTestingLibrary.BoundFunction<Q[P]>
39+
* }} RenderResult
40+
*/
41+
1142
export class SvelteTestingLibrary {
1243
svelteComponentOptions = [
1344
'target',
@@ -49,6 +80,17 @@ export class SvelteTestingLibrary {
4980
return { props: options }
5081
}
5182

83+
/**
84+
* Render a component into the document.
85+
*
86+
* @template {Svelte.SvelteComponent} C
87+
* @template {DOMTestingLibrary.Queries} [Q=typeof DOMTestingLibrary.queries]
88+
*
89+
* @param {Svelte.ComponentType<C>} Component - The component to render.
90+
* @param {SvelteComponentOptions<C>} componentOptions - Customize how Svelte renders the component.
91+
* @param {RenderOptions<Q>} renderOptions - Customize how Testing Library sets up the document and binds queries.
92+
* @returns {RenderResult<C, Q>} The rendered component and bound testing functions.
93+
*/
5294
render(Component, componentOptions = {}, renderOptions = {}) {
5395
componentOptions = this.checkProps(componentOptions)
5496

@@ -72,7 +114,7 @@ export class SvelteTestingLibrary {
72114
baseElement,
73115
component,
74116
container: target,
75-
debug: (el = baseElement) => console.log(prettyDOM(el)),
117+
debug: (el = baseElement) => console.log(DOMTestingLibrary.prettyDOM(el)),
76118
rerender: async (props) => {
77119
if (props.props) {
78120
console.warn(
@@ -86,7 +128,10 @@ export class SvelteTestingLibrary {
86128
unmount: () => {
87129
this.cleanupComponent(component)
88130
},
89-
...getQueriesForElement(baseElement, renderOptions.queries),
131+
...DOMTestingLibrary.getQueriesForElement(
132+
baseElement,
133+
renderOptions.queries
134+
),
90135
}
91136
}
92137

@@ -123,6 +168,9 @@ export class SvelteTestingLibrary {
123168
}
124169
}
125170

171+
/**
172+
* Unmount all components and remove elements added to `<body>`.
173+
*/
126174
cleanup() {
127175
this.componentCache.forEach(this.cleanupComponent.bind(this))
128176
this.targetCache.forEach(this.cleanupTarget.bind(this))
@@ -135,22 +183,46 @@ export const render = instance.render.bind(instance)
135183

136184
export const cleanup = instance.cleanup.bind(instance)
137185

186+
/**
187+
* Call a function and wait for Svelte to flush pending changes.
188+
*
189+
* @param {() => unknown} [fn] - A function, which may be `async`, to call before flushing updates.
190+
* @returns {Promise<void>}
191+
*/
138192
export const act = async (fn) => {
139193
if (fn) {
140194
await fn()
141195
}
142196
return Svelte.tick()
143197
}
144198

199+
/**
200+
* @typedef {(...args: Parameters<DOMTestingLibrary.FireFunction>) => Promise<ReturnType<DOMTestingLibrary.FireFunction>>} FireFunction
201+
*/
202+
203+
/**
204+
* @typedef {{
205+
* [K in DOMTestingLibrary.EventType]: (...args: Parameters<DOMTestingLibrary.FireObject[K]>) => Promise<ReturnType<DOMTestingLibrary.FireObject[K]>>
206+
* }} FireObject
207+
*/
208+
209+
/**
210+
* Fire an event on an element.
211+
*
212+
* Consider using `@testing-library/user-event` instead, if possible.
213+
* @see https://testing-library.com/docs/user-event/intro/
214+
*
215+
* @type {FireFunction & FireObject}
216+
*/
145217
export const fireEvent = async (...args) => {
146-
const event = dtlFireEvent(...args)
218+
const event = DOMTestingLibrary.fireEvent(...args)
147219
await Svelte.tick()
148220
return event
149221
}
150222

151-
Object.keys(dtlFireEvent).forEach((key) => {
223+
Object.keys(DOMTestingLibrary.fireEvent).forEach((key) => {
152224
fireEvent[key] = async (...args) => {
153-
const event = dtlFireEvent[key](...args)
225+
const event = DOMTestingLibrary.fireEvent[key](...args)
154226
await Svelte.tick()
155227
return event
156228
}

tsconfig.build.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"extends": ["./tsconfig.json"],
3+
"compilerOptions": {
4+
"declaration": true,
5+
"declarationMap": true,
6+
"emitDeclarationOnly": true,
7+
"noEmit": false,
8+
"rootDir": "src",
9+
"outDir": "types"
10+
},
11+
"exclude": ["src/**/__tests__/**"]
12+
}

tsconfig.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"compilerOptions": {
33
"module": "node16",
4+
"allowJs": true,
45
"noEmit": true,
56
"skipLibCheck": true,
67
"strict": true,
78
"types": ["svelte", "vite/client", "vitest", "vitest/globals"]
89
},
9-
"include": ["src", "types"]
10+
"include": ["src"]
1011
}

types/index.d.ts

-82
This file was deleted.

types/vite.d.ts

-12
This file was deleted.

0 commit comments

Comments
 (0)