Skip to content

Commit 979431a

Browse files
committed
refactor(types): build types from JS source
1 parent 9dce164 commit 979431a

File tree

9 files changed

+161
-104
lines changed

9 files changed

+161
-104
lines changed

.github/workflows/release.yml

Lines changed: 31 additions & 1 deletion
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 34 additions & 2 deletions
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

Lines changed: 77 additions & 4 deletions
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'
@@ -16,6 +16,52 @@ import {
1616
const targetCache = new Set()
1717
const componentCache = new Set()
1818

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

@@ -62,6 +108,7 @@ const render = (Component, options = {}, renderOptions = {}) => {
62108
}
63109
}
64110

111+
/** Remove a component from the component cache. */
65112
const cleanupComponent = (component) => {
66113
const inCache = componentCache.delete(component)
67114

@@ -70,6 +117,7 @@ const cleanupComponent = (component) => {
70117
}
71118
}
72119

120+
/** Remove a target element from the target cache. */
73121
const cleanupTarget = (target) => {
74122
const inCache = targetCache.delete(target)
75123

@@ -78,27 +126,52 @@ const cleanupTarget = (target) => {
78126
}
79127
}
80128

129+
/** Unmount all components and remove elements added to `<body>`. */
81130
const cleanup = () => {
82131
componentCache.forEach(cleanupComponent)
83132
targetCache.forEach(cleanupTarget)
84133
}
85134

135+
/**
136+
* Call a function and wait for Svelte to flush pending changes.
137+
*
138+
* @param {() => unknown} [fn] - A function, which may be `async`, to call before flushing updates.
139+
* @returns {Promise<void>}
140+
*/
86141
const act = async (fn) => {
87142
if (fn) {
88143
await fn()
89144
}
90145
return tick()
91146
}
92147

148+
/**
149+
* @typedef {(...args: Parameters<import('@testing-library/dom').FireFunction>) => Promise<ReturnType<import('@testing-library/dom').FireFunction>>} FireFunction
150+
*/
151+
152+
/**
153+
* @typedef {{
154+
* [K in import('@testing-library/dom').EventType]: (...args: Parameters<import('@testing-library/dom').FireObject[K]>) => Promise<ReturnType<import('@testing-library/dom').FireObject[K]>>
155+
* }} FireObject
156+
*/
157+
158+
/**
159+
* Fire an event on an element.
160+
*
161+
* Consider using `@testing-library/user-event` instead, if possible.
162+
* @see https://testing-library.com/docs/user-event/intro/
163+
*
164+
* @type {FireFunction & FireObject}
165+
*/
93166
const fireEvent = async (...args) => {
94-
const event = dtlFireEvent(...args)
167+
const event = baseFireEvent(...args)
95168
await tick()
96169
return event
97170
}
98171

99-
Object.keys(dtlFireEvent).forEach((key) => {
172+
Object.keys(baseFireEvent).forEach((key) => {
100173
fireEvent[key] = async (...args) => {
101-
const event = dtlFireEvent[key](...args)
174+
const event = baseFireEvent[key](...args)
102175
await tick()
103176
return event
104177
}

tsconfig.build.json

Lines changed: 12 additions & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 0 additions & 82 deletions
This file was deleted.

types/vite.d.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)