Skip to content

Commit

Permalink
refactor(types): build types from JS source
Browse files Browse the repository at this point in the history
  • Loading branch information
mcous committed May 28, 2024
1 parent cb66333 commit 5192455
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 109 deletions.
31 changes: 30 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,35 @@ jobs:
run: npm run test:${{ matrix.test-runner }}

- name: ▶️ Run type-checks
if: ${{ matrix.node == '20' && matrix.svelte == '4' && matrix.test-runner == 'vitest:jsdom' }}
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
runs-on: ubuntu-latest
Expand All @@ -83,6 +106,12 @@ jobs:
- name: 📥 Download deps
run: npm install --no-package-lock

- name: 📥 Downloads types build
uses: actions/download-artifact@v4
with:
name: types
path: types

- name: 🚀 Release
uses: cycjimmy/semantic-release-action@v2
with:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ dist
yarn-error.log
package-lock.json
yarn.lock

# generated typing output
types
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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:* types",
"build": "tsc -p tsconfig.build.json",
"validate": "npm-run-all test:vitest:* types build",
"contributors:add": "all-contributors add",
"contributors:generate": "all-contributors generate"
},
Expand Down
36 changes: 34 additions & 2 deletions types/types.test-d.ts → src/__tests__/types.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -62,4 +62,36 @@ describe('types', () => {

expectTypeOf(result.getByVibes).parameters.toMatchTypeOf<[vibes: string]>()
})

test('act is an async function', () => {
expectTypeOf(subject.act).toMatchTypeOf<() => Promise<void>>()
})

test('act accepts a sync function', () => {
expectTypeOf(subject.act).toMatchTypeOf<(fn: () => void) => Promise<void>>()
})

test('act accepts an async function', () => {
expectTypeOf(subject.act).toMatchTypeOf<
(fn: () => Promise<void>) => Promise<void>
>()
})

test('fireEvent is an async function', () => {
expectTypeOf(subject.fireEvent).toMatchTypeOf<
(
element: Element | Node | Document | Window,
event: Event
) => Promise<boolean>
>()
})

test('fireEvent[eventName] is an async function', () => {
expectTypeOf(subject.fireEvent.click).toMatchTypeOf<
(
element: Element | Node | Document | Window,
options?: {}
) => Promise<boolean>
>()
})
})
92 changes: 82 additions & 10 deletions src/pure.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,44 @@
import {
fireEvent as dtlFireEvent,
getQueriesForElement,
prettyDOM,
} from '@testing-library/dom'
import * as DOMTestingLibrary 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)

/**
* Customize how Svelte renders the component.
*
* @template {Svelte.SvelteComponent} C
* @typedef {Svelte.ComponentProps<C> | Partial<Svelte.ComponentConstructorOptions<Svelte.ComponentProps<C>>>} SvelteComponentOptions
*/

/**
* Customize how Testing Library sets up the document and binds queries.
*
* @template {DOMTestingLibrary.Queries} [Q=typeof DOMTestingLibrary.queries]
* @typedef {{
* baseElement?: HTMLElement
* queries?: Q
* }} RenderOptions
*/

/**
* The rendered component and bound testing functions.
*
* @template {Svelte.SvelteComponent} C
* @template {DOMTestingLibrary.Queries} [Q=typeof DOMTestingLibrary.queries]
*
* @typedef {{
* container: HTMLElement
* baseElement: HTMLElement
* component: C
* debug: (el?: HTMLElement | DocumentFragment) => void
* rerender: (props: Partial<Svelte.ComponentProps<C>>) => Promise<void>
* unmount: () => void
* } & {
* [P in keyof Q]: DOMTestingLibrary.BoundFunction<Q[P]>
* }} RenderResult
*/

export class SvelteTestingLibrary {
svelteComponentOptions = [
'target',
Expand Down Expand Up @@ -49,6 +80,17 @@ export class SvelteTestingLibrary {
return { props: options }
}

/**
* Render a component into the document.
*
* @template {Svelte.SvelteComponent} C
* @template {DOMTestingLibrary.Queries} [Q=typeof DOMTestingLibrary.queries]
*
* @param {Svelte.ComponentType<C>} Component - The component to render.
* @param {SvelteComponentOptions<C>} componentOptions - Customize how Svelte renders the component.
* @param {RenderOptions<Q>} renderOptions - Customize how Testing Library sets up the document and binds queries.
* @returns {RenderResult<C, Q>} The rendered component and bound testing functions.
*/
render(Component, componentOptions = {}, renderOptions = {}) {
componentOptions = this.checkProps(componentOptions)

Expand All @@ -72,7 +114,7 @@ export class SvelteTestingLibrary {
baseElement,
component,
container: target,
debug: (el = baseElement) => console.log(prettyDOM(el)),
debug: (el = baseElement) => console.log(DOMTestingLibrary.prettyDOM(el)),
rerender: async (props) => {
if (props.props) {
console.warn(
Expand All @@ -86,7 +128,10 @@ export class SvelteTestingLibrary {
unmount: () => {
this.cleanupComponent(component)
},
...getQueriesForElement(baseElement, renderOptions.queries),
...DOMTestingLibrary.getQueriesForElement(
baseElement,
renderOptions.queries
),
}
}

Expand Down Expand Up @@ -123,6 +168,9 @@ export class SvelteTestingLibrary {
}
}

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

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

/**
* 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<void>}
*/
export const act = async (fn) => {
if (fn) {
await fn()
}
return Svelte.tick()
}

/**
* @typedef {(...args: Parameters<DOMTestingLibrary.FireFunction>) => Promise<ReturnType<DOMTestingLibrary.FireFunction>>} FireFunction
*/

/**
* @typedef {{
* [K in DOMTestingLibrary.EventType]: (...args: Parameters<DOMTestingLibrary.FireObject[K]>) => Promise<ReturnType<DOMTestingLibrary.FireObject[K]>>
* }} 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}
*/
export const fireEvent = async (...args) => {
const event = dtlFireEvent(...args)
const event = DOMTestingLibrary.fireEvent(...args)
await Svelte.tick()
return event
}

Object.keys(dtlFireEvent).forEach((key) => {
Object.keys(DOMTestingLibrary.fireEvent).forEach((key) => {
fireEvent[key] = async (...args) => {
const event = dtlFireEvent[key](...args)
const event = DOMTestingLibrary.fireEvent[key](...args)
await Svelte.tick()
return event
}
Expand Down
12 changes: 12 additions & 0 deletions tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": ["./tsconfig.json"],
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"noEmit": false,
"rootDir": "src",
"outDir": "types"
},
"exclude": ["src/**/__tests__/**"]
}
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -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"]
}
82 changes: 0 additions & 82 deletions types/index.d.ts

This file was deleted.

12 changes: 0 additions & 12 deletions types/vite.d.ts

This file was deleted.

0 comments on commit 5192455

Please sign in to comment.