Skip to content

Commit 087a3c6

Browse files
committed
refactor(types): build types from JS source
1 parent 18b118b commit 087a3c6

File tree

9 files changed

+164
-105
lines changed

9 files changed

+164
-105
lines changed

.github/workflows/release.yml

+31-1
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,36 @@ 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+
# NOTE: `SvelteComponent` is not generic in Svelte v3, so type-checking will not pass
63+
if: ${{ matrix.node == '20' && matrix.svelte != '3' && matrix.test-runner == 'vitest:jsdom' }}
6364
run: npm run types
6465

6566
- name: ⬆️ Upload coverage report
6667
uses: codecov/codecov-action@v3
6768

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

107+
- name: 📥 Downloads types build
108+
uses: actions/download-artifact@v4
109+
with:
110+
name: types
111+
path: types
112+
83113
- name: 🚀 Release
84114
uses: cycjimmy/semantic-release-action@v4
85115
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-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
"files": [
5050
"src",
5151
"types",
52-
"!*.test-d.ts",
5352
"!__tests__"
5453
],
5554
"scripts": {
@@ -69,7 +68,8 @@
6968
"test:vitest:happy-dom": "vitest run --coverage --environment happy-dom",
7069
"test:jest": "npx --node-options=\"--experimental-vm-modules --no-warnings\" jest --coverage",
7170
"types": "svelte-check",
72-
"validate": "npm-run-all test:vitest:* test:jest types",
71+
"validate": "npm-run-all test:vitest:* test:jest types build",
72+
"build": "tsc -p tsconfig.build.json",
7373
"contributors:add": "all-contributors add",
7474
"contributors:generate": "all-contributors generate",
7575
"preview-release": "./scripts/preview-release"

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

+80-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
fireEvent as dtlFireEvent,
2+
fireEvent as baseFireEvent,
33
getQueriesForElement,
44
prettyDOM,
55
} from '@testing-library/dom'
@@ -10,6 +10,52 @@ import { mount, unmount, updateProps, validateOptions } from './core/index.js'
1010
const targetCache = new Set()
1111
const componentCache = new Set()
1212

13+
/**
14+
* Customize how Svelte renders the component.
15+
*
16+
* @template {import('svelte').SvelteComponent} C
17+
* @typedef {import('svelte').ComponentProps<C> | Partial<import('svelte').ComponentConstructorOptions<import('svelte').ComponentProps<C>>>} SvelteComponentOptions
18+
*/
19+
20+
/**
21+
* Customize how Testing Library sets up the document and binds queries.
22+
*
23+
* @template {import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries]
24+
* @typedef {{
25+
* baseElement?: HTMLElement
26+
* queries?: Q
27+
* }} RenderOptions
28+
*/
29+
30+
/**
31+
* The rendered component and bound testing functions.
32+
*
33+
* @template {import('svelte').SvelteComponent} C
34+
* @template {import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries]
35+
*
36+
* @typedef {{
37+
* container: HTMLElement
38+
* baseElement: HTMLElement
39+
* component: C
40+
* debug: (el?: HTMLElement | DocumentFragment) => void
41+
* rerender: (props: Partial<import('svelte').ComponentProps<C>>) => Promise<void>
42+
* unmount: () => void
43+
* } & {
44+
* [P in keyof Q]: import('@testing-library/dom').BoundFunction<Q[P]>
45+
* }} RenderResult
46+
*/
47+
48+
/**
49+
* Render a component into the document.
50+
*
51+
* @template {import('svelte').SvelteComponent} C
52+
* @template {import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries]
53+
*
54+
* @param {import('svelte').ComponentType<C>} Component - The component to render.
55+
* @param {SvelteComponentOptions<C>} options - Customize how Svelte renders the component.
56+
* @param {RenderOptions<Q>} renderOptions - Customize how Testing Library sets up the document and binds queries.
57+
* @returns {RenderResult<C, Q>} The rendered component and bound testing functions.
58+
*/
1359
const render = (Component, options = {}, renderOptions = {}) => {
1460
options = validateOptions(options)
1561

@@ -33,7 +79,9 @@ const render = (Component, options = {}, renderOptions = {}) => {
3379
baseElement,
3480
component,
3581
container: target,
36-
debug: (el = baseElement) => console.log(prettyDOM(el)),
82+
debug: (el = baseElement) => {
83+
console.log(prettyDOM(el))
84+
},
3785
rerender: async (props) => {
3886
if (props.props) {
3987
console.warn(
@@ -52,6 +100,7 @@ const render = (Component, options = {}, renderOptions = {}) => {
52100
}
53101
}
54102

103+
/** Remove a component from the component cache. */
55104
const cleanupComponent = (component) => {
56105
const inCache = componentCache.delete(component)
57106

@@ -60,6 +109,7 @@ const cleanupComponent = (component) => {
60109
}
61110
}
62111

112+
/** Remove a target element from the target cache. */
63113
const cleanupTarget = (target) => {
64114
const inCache = targetCache.delete(target)
65115

@@ -68,27 +118,52 @@ const cleanupTarget = (target) => {
68118
}
69119
}
70120

121+
/** Unmount all components and remove elements added to `<body>`. */
71122
const cleanup = () => {
72123
componentCache.forEach(cleanupComponent)
73124
targetCache.forEach(cleanupTarget)
74125
}
75126

127+
/**
128+
* Call a function and wait for Svelte to flush pending changes.
129+
*
130+
* @param {() => unknown} [fn] - A function, which may be `async`, to call before flushing updates.
131+
* @returns {Promise<void>}
132+
*/
76133
const act = async (fn) => {
77134
if (fn) {
78135
await fn()
79136
}
80137
return tick()
81138
}
82139

140+
/**
141+
* @typedef {(...args: Parameters<import('@testing-library/dom').FireFunction>) => Promise<ReturnType<import('@testing-library/dom').FireFunction>>} FireFunction
142+
*/
143+
144+
/**
145+
* @typedef {{
146+
* [K in import('@testing-library/dom').EventType]: (...args: Parameters<import('@testing-library/dom').FireObject[K]>) => Promise<ReturnType<import('@testing-library/dom').FireObject[K]>>
147+
* }} FireObject
148+
*/
149+
150+
/**
151+
* Fire an event on an element.
152+
*
153+
* Consider using `@testing-library/user-event` instead, if possible.
154+
* @see https://testing-library.com/docs/user-event/intro/
155+
*
156+
* @type {FireFunction & FireObject}
157+
*/
83158
const fireEvent = async (...args) => {
84-
const event = dtlFireEvent(...args)
159+
const event = baseFireEvent(...args)
85160
await tick()
86161
return event
87162
}
88163

89-
Object.keys(dtlFireEvent).forEach((key) => {
164+
Object.keys(baseFireEvent).forEach((key) => {
90165
fireEvent[key] = async (...args) => {
91-
const event = dtlFireEvent[key](...args)
166+
const event = baseFireEvent[key](...args)
92167
await tick()
93168
return event
94169
}

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)