@untemps/react-vocal 1.7.34 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.github/workflows/publish.yml +2 -0
  2. package/.husky/commit-msg +1 -0
  3. package/.husky/pre-commit +1 -0
  4. package/.prettierignore +1 -0
  5. package/CHANGELOG.md +24 -0
  6. package/CLAUDE.md +12 -8
  7. package/README.md +23 -16
  8. package/dev/index.html +24 -0
  9. package/dev/package.json +12 -8
  10. package/dev/src/{index.js → index.jsx} +2 -3
  11. package/dev/vite.config.js +10 -0
  12. package/dev/yarn.lock +3 -3
  13. package/dist/index.es.js +2151 -2
  14. package/dist/index.es.js.map +1 -0
  15. package/dist/index.js +9 -2
  16. package/dist/index.js.map +1 -0
  17. package/dist/index.umd.js +9 -2
  18. package/dist/index.umd.js.map +1 -0
  19. package/package.json +27 -60
  20. package/src/components/{Icon.js → Icon.jsx} +1 -14
  21. package/src/components/Vocal.jsx +168 -0
  22. package/src/components/__tests__/{Icon.test.js → Icon.test.jsx} +0 -4
  23. package/src/components/__tests__/{Vocal.test.js → Vocal.test.jsx} +15 -18
  24. package/src/components/__tests__/{VocalWithMockedUseVocal.test.js → VocalWithMockedUseVocal.test.jsx} +11 -13
  25. package/src/components/__tests__/__snapshots__/Icon.test.jsx.snap +21 -0
  26. package/src/components/__tests__/__snapshots__/Vocal.test.jsx.snap +28 -0
  27. package/src/hooks/__tests__/useCommands.test.js +1 -1
  28. package/src/hooks/__tests__/useTimeout.test.js +6 -6
  29. package/src/hooks/__tests__/useVocal.test.js +15 -15
  30. package/src/hooks/useTimeout.js +0 -2
  31. package/src/hooks/useVocal.js +0 -2
  32. package/vite.config.js +35 -0
  33. package/vitest.setup.js +71 -0
  34. package/babel.config.js +0 -12
  35. package/dev/babel.config.js +0 -4
  36. package/dev/rollup.config.js +0 -29
  37. package/jest/jest.setup.js +0 -72
  38. package/rollup.config.js +0 -42
  39. package/src/components/Vocal.js +0 -235
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@untemps/react-vocal",
3
- "version": "1.7.34",
3
+ "version": "2.0.0-beta.1",
4
4
  "author": "Vincent Le Badezet <v.lebadezet@untemps.net>",
5
5
  "repository": "git@github.com:untemps/react-vocal.git",
6
6
  "license": "MIT",
@@ -23,67 +23,36 @@
23
23
  "main": "dist/index.js",
24
24
  "module": "dist/index.es.js",
25
25
  "engines": {
26
- "node": ">=18.0.0"
26
+ "node": "^20.19.0 || >=22.12.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@babel/cli": "^7.12.10",
30
- "@babel/core": "^7.12.10",
31
- "@babel/plugin-proposal-class-properties": "^7.12.1",
32
- "@babel/plugin-transform-react-jsx": "^7.12.12",
33
- "@babel/plugin-transform-runtime": "^7.12.10",
34
- "@babel/preset-env": "^7.12.11",
35
- "@babel/preset-react": "^7.12.10",
36
- "@commitlint/cli": "^9.1.1",
37
- "@commitlint/config-conventional": "^9.1.1",
38
- "@rollup/plugin-babel": "^5.2.2",
39
- "@rollup/plugin-commonjs": "^17.0.0",
40
- "@rollup/plugin-node-resolve": "^11.0.1",
29
+ "@commitlint/cli": "^20.5.3",
30
+ "@commitlint/config-conventional": "^20.5.3",
41
31
  "@semantic-release/changelog": "^6.0.3",
42
32
  "@semantic-release/git": "^10.0.1",
43
33
  "@semantic-release/github": "^12.0.6",
44
- "@testing-library/dom": "^7.29.0",
45
- "@testing-library/jest-dom": "^5.11.6",
46
- "@testing-library/react": "^11.2.2",
47
- "@testing-library/react-hooks": "^3.7.0",
48
- "@untemps/utils": "^2.0.0-beta.1",
49
- "babel-jest": "^28.0.0-alpha.11",
50
- "cross-env": "^7.0.3",
51
- "husky": "^4.3.6",
52
- "jest": "^28.0.0-alpha.11",
53
- "jest-environment-jsdom": "^28.0.0-alpha.11",
54
- "prettier": "^2.2.1",
55
- "prop-types": "^15.7.2",
56
- "react": "^17.0.1",
57
- "react-dom": "^17.0.1",
58
- "react-test-renderer": "^17.0.1",
59
- "rimraf": "^3.0.2",
60
- "rollup": "^2.35.1",
61
- "rollup-plugin-sizes": "^1.0.4",
62
- "rollup-plugin-terser": "^7.0.2",
63
- "rollup-plugin-visualizer": "^5.14.0",
64
- "semantic-release": "^25.0.3"
34
+ "@testing-library/dom": "^10.4.1",
35
+ "@testing-library/jest-dom": "^6.9.1",
36
+ "@testing-library/react": "^16.3.2",
37
+ "@untemps/utils": "^3.2.0",
38
+ "@vitejs/plugin-react": "^6.0.1",
39
+ "@vitest/coverage-v8": "^4.1.5",
40
+ "husky": "^9.1.7",
41
+ "jsdom": "^29.1.1",
42
+ "prettier": "^3.8.3",
43
+ "react": "^19.2.5",
44
+ "react-dom": "^19.2.5",
45
+ "semantic-release": "^25.0.3",
46
+ "vite": "^8.0.10",
47
+ "vitest": "^4.1.5"
65
48
  },
66
49
  "peerDependencies": {
67
- "react": "^16.13.1",
68
- "react-dom": "^16.13.1"
50
+ "react": ">=16.13.1",
51
+ "react-dom": ">=16.13.1"
69
52
  },
70
53
  "dependencies": {
71
54
  "@untemps/vocal": "^1.3.0",
72
- "fuse.js": "^6.4.6"
73
- },
74
- "jest": {
75
- "coverageDirectory": "./coverage/",
76
- "collectCoverage": true,
77
- "setupFilesAfterEnv": [
78
- "<rootDir>/jest/jest.setup.js"
79
- ],
80
- "restoreMocks": true
81
- },
82
- "husky": {
83
- "hooks": {
84
- "pre-commit": "yarn test:ci && yarn prettier",
85
- "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
86
- }
55
+ "fuse.js": "^7.3.0"
87
56
  },
88
57
  "release": {
89
58
  "branches": [
@@ -131,13 +100,11 @@
131
100
  ]
132
101
  },
133
102
  "scripts": {
134
- "dev": "cd dev && rimraf dist && yarn && yarn start",
135
- "test": "jest -u --watch",
136
- "test:ci": "jest -u -b --ci --coverage",
137
- "build": "rimraf dist && yarn build:cjs && yarn build:es && yarn build:umd",
138
- "build:cjs": "cross-env NODE_ENV=production BABEL_ENV=cjs rollup -c",
139
- "build:es": "cross-env NODE_ENV=production BABEL_ENV=es rollup -c",
140
- "build:umd": "cross-env NODE_ENV=production BABEL_ENV=umd rollup -c",
141
- "prettier": "prettier \"*/**/*.js\" --ignore-path ./.prettierignore --write && git add . && git status"
103
+ "dev": "cd dev && yarn && yarn dev",
104
+ "test": "vitest",
105
+ "test:ci": "vitest run --coverage",
106
+ "build": "vite build",
107
+ "prepare": "husky",
108
+ "prettier": "prettier \"*/**/*.js\" --ignore-path ./.prettierignore --write && git add -u && git status"
142
109
  }
143
110
  }
@@ -1,7 +1,6 @@
1
1
  import React from 'react'
2
- import PropTypes from 'prop-types'
3
2
 
4
- const Icon = ({ color, activeColor, isActive }) => {
3
+ const Icon = ({ color = 'black', activeColor = 'red', isActive = false }) => {
5
4
  return (
6
5
  <svg
7
6
  data-testid="__icon-root__"
@@ -22,16 +21,4 @@ const Icon = ({ color, activeColor, isActive }) => {
22
21
  )
23
22
  }
24
23
 
25
- Icon.propTypes = {
26
- color: PropTypes.string,
27
- activeColor: PropTypes.string,
28
- isActive: PropTypes.bool,
29
- }
30
-
31
- Icon.defaultProps = {
32
- color: 'black',
33
- activeColor: 'red',
34
- isActive: false,
35
- }
36
-
37
24
  export default Icon
@@ -0,0 +1,168 @@
1
+ import React, { cloneElement, isValidElement, useRef, useState } from 'react'
2
+ import { Vocal as SpeechRecognitionWrapper } from '@untemps/vocal'
3
+ import { isFunction } from '@untemps/utils/function/isFunction'
4
+
5
+ import useVocal from '../hooks/useVocal'
6
+ import useTimeout from '../hooks/useTimeout'
7
+ import useCommands from '../hooks/useCommands'
8
+
9
+ import Icon from './Icon'
10
+
11
+ const Vocal = ({
12
+ children,
13
+ commands = null,
14
+ lang = 'en-US',
15
+ grammars = null,
16
+ timeout = 3000,
17
+ ariaLabel = 'start recognition',
18
+ style = null,
19
+ className = null,
20
+ outlineStyle = '2px solid',
21
+ onStart = null,
22
+ onEnd = null,
23
+ onSpeechStart = null,
24
+ onSpeechEnd = null,
25
+ onResult = null,
26
+ onError = null,
27
+ onNoMatch = null,
28
+ __rsInstance,
29
+ }) => {
30
+ const buttonRef = useRef(null)
31
+ const [isListening, setIsListening] = useState(false)
32
+
33
+ const [, { start, stop, subscribe, unsubscribe }] = useVocal(lang, grammars, __rsInstance)
34
+ const triggerCommand = useCommands(commands)
35
+
36
+ const _onEnd = (e) => {
37
+ stopTimer()
38
+ stopRecognition()
39
+ unsubscribeAll()
40
+ onEnd?.(e)
41
+ }
42
+
43
+ const [startTimer, stopTimer] = useTimeout(_onEnd, timeout)
44
+
45
+ const startRecognition = () => {
46
+ try {
47
+ setIsListening(true)
48
+ subscribeAll()
49
+ start()
50
+ } catch (error) {
51
+ _onError(error)
52
+ }
53
+ }
54
+
55
+ const stopRecognition = () => {
56
+ try {
57
+ setIsListening(false)
58
+ stop()
59
+ } catch (error) {
60
+ onError?.(error)
61
+ }
62
+ }
63
+
64
+ const _onFocus = () => {
65
+ if (!className && outlineStyle) {
66
+ buttonRef.current.style.outline = outlineStyle
67
+ }
68
+ }
69
+
70
+ const _onBlur = () => {
71
+ if (!className && outlineStyle) {
72
+ buttonRef.current.style.outline = 'none'
73
+ }
74
+ }
75
+
76
+ const _onStart = (e) => {
77
+ startTimer()
78
+ onStart?.(e)
79
+ }
80
+
81
+ const _onSpeechStart = (e) => {
82
+ stopTimer()
83
+ onSpeechStart?.(e)
84
+ }
85
+
86
+ const _onSpeechEnd = (e) => {
87
+ startTimer()
88
+ onSpeechEnd?.(e)
89
+ }
90
+
91
+ const _onResult = (event, result) => {
92
+ stopTimer()
93
+ stopRecognition()
94
+ triggerCommand(result)
95
+ onResult?.(result, event)
96
+ }
97
+
98
+ const _onError = (error) => {
99
+ stopRecognition()
100
+ onError?.(error)
101
+ }
102
+
103
+ const _onNoMatch = (e) => {
104
+ stopTimer()
105
+ stopRecognition()
106
+ onNoMatch?.(e)
107
+ }
108
+
109
+ const HANDLERS = {
110
+ start: _onStart,
111
+ end: _onEnd,
112
+ speechstart: _onSpeechStart,
113
+ speechend: _onSpeechEnd,
114
+ result: _onResult,
115
+ error: _onError,
116
+ nomatch: _onNoMatch,
117
+ }
118
+
119
+ const subscribeAll = () => Object.entries(HANDLERS).forEach(([event, handler]) => subscribe(event, handler))
120
+ const unsubscribeAll = () => Object.entries(HANDLERS).forEach(([event, handler]) => unsubscribe(event, handler))
121
+
122
+ const _renderDefault = () => (
123
+ <button
124
+ data-testid="__vocal-root__"
125
+ ref={buttonRef}
126
+ role="button"
127
+ aria-label={ariaLabel}
128
+ style={
129
+ className
130
+ ? null
131
+ : {
132
+ width: 24,
133
+ height: 24,
134
+ backgroundColor: 'transparent', // `background: none` shorthand resets all sub-properties; jsdom 29 + jest-dom v6 don't reflect that correctly via getComputedStyle
135
+ border: 'none',
136
+ padding: 0,
137
+ cursor: !isListening ? 'pointer' : 'default',
138
+ ...style,
139
+ }
140
+ }
141
+ className={className}
142
+ onFocus={_onFocus}
143
+ onBlur={_onBlur}
144
+ onClick={startRecognition}
145
+ >
146
+ <Icon isActive={isListening} color="#aaa" />
147
+ </button>
148
+ )
149
+
150
+ const _renderChildren = () => {
151
+ if (SpeechRecognitionWrapper.isSupported) {
152
+ if (isFunction(children)) {
153
+ return children(startRecognition, stopRecognition, isListening)
154
+ } else if (isValidElement(children)) {
155
+ return cloneElement(children, {
156
+ ...(!isListening && { onClick: startRecognition }),
157
+ })
158
+ } else {
159
+ return _renderDefault()
160
+ }
161
+ }
162
+ return null
163
+ }
164
+
165
+ return _renderChildren()
166
+ }
167
+
168
+ export default Vocal
@@ -1,7 +1,3 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
-
5
1
  import React from 'react'
6
2
  import { render } from '@testing-library/react'
7
3
 
@@ -1,7 +1,3 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
-
5
1
  import React from 'react'
6
2
  import { waitFor } from '@testing-library/dom'
7
3
  import { act, fireEvent, render } from '@testing-library/react'
@@ -34,11 +30,11 @@ describe('Vocal', () => {
34
30
  })
35
31
 
36
32
  it('renders no children element if SpeechRecognition is not supported', () => {
37
- jest.spyOn(SpeechRecognitionWrapper, 'isSupported', 'get').mockReturnValueOnce(false)
33
+ vi.spyOn(SpeechRecognitionWrapper, 'isSupported', 'get').mockReturnValueOnce(false)
38
34
  const { queryByTestId } = render(getInstance(null, <div data-testid="__vocal-custom-root__" />))
39
35
  expect(queryByTestId('__vocal-root__')).not.toBeInTheDocument()
40
36
  expect(queryByTestId('__vocal-custom-root__')).not.toBeInTheDocument()
41
- jest.clearAllMocks()
37
+ vi.clearAllMocks()
42
38
  })
43
39
 
44
40
  it('renders custom children function', () => {
@@ -48,7 +44,7 @@ describe('Vocal', () => {
48
44
  })
49
45
 
50
46
  it('starts recognition with custom children function', async () => {
51
- const onStart = jest.fn()
47
+ const onStart = vi.fn()
52
48
  const { queryByTestId } = render(
53
49
  getInstance({ onStart }, (start) => <div data-testid="__vocal-custom-root__" onClick={start} />)
54
50
  )
@@ -59,7 +55,7 @@ describe('Vocal', () => {
59
55
  })
60
56
 
61
57
  it('stops recognition with custom children function', async () => {
62
- const onEnd = jest.fn()
58
+ const onEnd = vi.fn()
63
59
  const { queryByText } = render(
64
60
  getInstance({ onEnd }, (start, stop) => (
65
61
  <div data-testid="__vocal-custom-root__">
@@ -76,7 +72,7 @@ describe('Vocal', () => {
76
72
  })
77
73
 
78
74
  it('gets recognition status with custom children function', async () => {
79
- const onEnd = jest.fn()
75
+ const onEnd = vi.fn()
80
76
  const { queryByText } = render(
81
77
  getInstance({ onEnd }, (start, stop, isStarted) => (
82
78
  <div data-testid="__vocal-custom-root__">
@@ -128,11 +124,12 @@ describe('Vocal', () => {
128
124
 
129
125
  it('uses custom styles', () => {
130
126
  const { getByTestId } = render(getInstance({ style: { backgroundColor: 'blue' } }))
131
- expect(getByTestId('__vocal-root__')).toHaveStyle({ backgroundColor: 'blue' })
127
+ // jest-dom v6 + jsdom 29: `div.style.color = 'blue'` reads back as 'blue' but getComputedStyle returns RGB; normalisation no longer bridges the gap
128
+ expect(getByTestId('__vocal-root__')).toHaveStyle({ backgroundColor: 'rgb(0, 0, 255)' })
132
129
  })
133
130
 
134
131
  it('responds to command', async () => {
135
- const callback = jest.fn()
132
+ const callback = vi.fn()
136
133
  const recognition = new SpeechRecognitionWrapper()
137
134
  const commands = { foo: callback }
138
135
  const { getByTestId } = render(getInstance({ __rsInstance: recognition, commands }))
@@ -153,7 +150,7 @@ describe('Vocal', () => {
153
150
  })
154
151
 
155
152
  it('triggers onStart handler', async () => {
156
- const onStart = jest.fn()
153
+ const onStart = vi.fn()
157
154
  const { queryByTestId } = render(getInstance({ onStart }))
158
155
  await act(async () => {
159
156
  fireEvent.click(queryByTestId('__vocal-root__'))
@@ -162,7 +159,7 @@ describe('Vocal', () => {
162
159
  })
163
160
 
164
161
  it('triggers onResult handler', async () => {
165
- const onResult = jest.fn()
162
+ const onResult = vi.fn()
166
163
  const recognition = new SpeechRecognitionWrapper()
167
164
  const { getByTestId } = render(getInstance({ __rsInstance: recognition, onResult }))
168
165
 
@@ -182,7 +179,7 @@ describe('Vocal', () => {
182
179
  })
183
180
 
184
181
  it('triggers onNoMatch handler', async () => {
185
- const onNoMatch = jest.fn()
182
+ const onNoMatch = vi.fn()
186
183
  const recognition = new SpeechRecognitionWrapper()
187
184
  const { getByTestId } = render(getInstance({ __rsInstance: recognition, onNoMatch }))
188
185
 
@@ -202,7 +199,7 @@ describe('Vocal', () => {
202
199
  })
203
200
 
204
201
  it('triggers onSpeechStart handler', async () => {
205
- const onSpeechStart = jest.fn()
202
+ const onSpeechStart = vi.fn()
206
203
  const recognition = new SpeechRecognitionWrapper()
207
204
  const { getByTestId } = render(getInstance({ __rsInstance: recognition, onSpeechStart }))
208
205
 
@@ -222,7 +219,7 @@ describe('Vocal', () => {
222
219
  })
223
220
 
224
221
  it('triggers onSpeechEnd handler', async () => {
225
- const onSpeechEnd = jest.fn()
222
+ const onSpeechEnd = vi.fn()
226
223
  const recognition = new SpeechRecognitionWrapper()
227
224
  const { getByTestId } = render(getInstance({ __rsInstance: recognition, onSpeechEnd }))
228
225
 
@@ -243,7 +240,7 @@ describe('Vocal', () => {
243
240
 
244
241
  it('triggers onEnd handler after timeout', async () => {
245
242
  const timeout = 100
246
- const onEnd = jest.fn()
243
+ const onEnd = vi.fn()
247
244
  const { getByTestId } = render(getInstance({ timeout, onEnd }))
248
245
  await act(async () => {
249
246
  fireEvent.click(getByTestId('__vocal-root__'))
@@ -252,7 +249,7 @@ describe('Vocal', () => {
252
249
  })
253
250
 
254
251
  it('triggers onEnd handler after speech', async () => {
255
- const onEnd = jest.fn()
252
+ const onEnd = vi.fn()
256
253
  const recognition = new SpeechRecognitionWrapper()
257
254
  const { getByTestId } = render(getInstance({ __rsInstance: recognition, onEnd }))
258
255
 
@@ -1,22 +1,20 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
-
5
1
  import React from 'react'
6
2
  import { waitFor } from '@testing-library/dom'
7
3
  import { act, fireEvent, render } from '@testing-library/react'
8
4
 
9
5
  import Vocal from '../Vocal'
10
6
 
11
- jest.mock('../../hooks/useVocal', () => {
12
- return () => [
13
- null,
14
- {
15
- subscribe: () => {
16
- throw new Error('Foo')
7
+ vi.mock('../../hooks/useVocal', () => {
8
+ return {
9
+ default: () => [
10
+ null,
11
+ {
12
+ subscribe: () => {
13
+ throw new Error('Foo')
14
+ },
17
15
  },
18
- },
19
- ]
16
+ ],
17
+ }
20
18
  })
21
19
 
22
20
  const defaultProps = {}
@@ -28,7 +26,7 @@ const getInstance = (props = {}, children = null) => (
28
26
 
29
27
  describe('Vocal', () => {
30
28
  it('triggers onError handler', async () => {
31
- const onError = jest.fn()
29
+ const onError = vi.fn()
32
30
  const { queryByTestId } = render(getInstance({ onError }))
33
31
  await act(async () => {
34
32
  fireEvent.click(queryByTestId('__vocal-root__'))
@@ -0,0 +1,21 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Icon > matches snapshot 1`] = `
4
+ <DocumentFragment>
5
+ <svg
6
+ data-testid="__icon-root__"
7
+ height="100%"
8
+ viewBox="0 0 24 24"
9
+ width="100%"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ >
12
+ <g>
13
+ <path
14
+ d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"
15
+ data-testid="__icon-path__"
16
+ fill="black"
17
+ />
18
+ </g>
19
+ </svg>
20
+ </DocumentFragment>
21
+ `;
@@ -0,0 +1,28 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Vocal > matches snapshot 1`] = `
4
+ <DocumentFragment>
5
+ <button
6
+ aria-label="start recognition"
7
+ data-testid="__vocal-root__"
8
+ role="button"
9
+ style="width: 24px; height: 24px; background-color: transparent; border: medium; padding: 0px; cursor: pointer;"
10
+ >
11
+ <svg
12
+ data-testid="__icon-root__"
13
+ height="100%"
14
+ viewBox="0 0 24 24"
15
+ width="100%"
16
+ xmlns="http://www.w3.org/2000/svg"
17
+ >
18
+ <g>
19
+ <path
20
+ d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"
21
+ data-testid="__icon-path__"
22
+ fill="#aaa"
23
+ />
24
+ </g>
25
+ </svg>
26
+ </button>
27
+ </DocumentFragment>
28
+ `;
@@ -1,4 +1,4 @@
1
- import { renderHook } from '@testing-library/react-hooks'
1
+ import { renderHook } from '@testing-library/react'
2
2
 
3
3
  import useCommands from '../useCommands'
4
4
 
@@ -1,4 +1,4 @@
1
- import { renderHook } from '@testing-library/react-hooks'
1
+ import { renderHook } from '@testing-library/react'
2
2
 
3
3
  import useTimeout from '../useTimeout'
4
4
 
@@ -10,13 +10,13 @@ const wait = (delay) => {
10
10
 
11
11
  describe('useTimeout', () => {
12
12
  it('not triggers handler before calling start', () => {
13
- const handler = jest.fn()
13
+ const handler = vi.fn()
14
14
  renderHook(() => useTimeout(handler))
15
15
  expect(handler).not.toHaveBeenCalled()
16
16
  })
17
17
 
18
18
  it('not triggers handler before timeout', async () => {
19
- const handler = jest.fn()
19
+ const handler = vi.fn()
20
20
  const timeout = 500
21
21
  const {
22
22
  result: {
@@ -29,7 +29,7 @@ describe('useTimeout', () => {
29
29
  })
30
30
 
31
31
  it('triggers handler immediately', async () => {
32
- const handler = jest.fn()
32
+ const handler = vi.fn()
33
33
  const {
34
34
  result: {
35
35
  current: [start],
@@ -41,7 +41,7 @@ describe('useTimeout', () => {
41
41
  })
42
42
 
43
43
  it('triggers handler after delay', async () => {
44
- const handler = jest.fn()
44
+ const handler = vi.fn()
45
45
  const timeout = 500
46
46
  const {
47
47
  result: {
@@ -54,7 +54,7 @@ describe('useTimeout', () => {
54
54
  })
55
55
 
56
56
  it('not triggers handler if stop is called before timeout', async () => {
57
- const handler = jest.fn()
57
+ const handler = vi.fn()
58
58
  const timeout = 500
59
59
  const {
60
60
  result: {
@@ -1,19 +1,19 @@
1
- import { renderHook } from '@testing-library/react-hooks'
1
+ import { renderHook } from '@testing-library/react'
2
2
  import { Vocal as SpeechRecognitionWrapper } from '@untemps/vocal'
3
3
 
4
4
  import useVocal from '../useVocal'
5
5
 
6
- jest.mock('@untemps/vocal')
6
+ vi.mock('@untemps/vocal')
7
7
 
8
8
  describe('useVocal', () => {
9
- const mockStart = jest.fn()
10
- const mockStop = jest.fn()
11
- const mockAbort = jest.fn()
12
- const mockAddEventListener = jest.fn()
13
- const mockRemoveEventListener = jest.fn()
14
- const mockCleanup = jest.fn()
15
-
16
- const mockIsSupported = jest.fn()
9
+ const mockStart = vi.fn()
10
+ const mockStop = vi.fn()
11
+ const mockAbort = vi.fn()
12
+ const mockAddEventListener = vi.fn()
13
+ const mockRemoveEventListener = vi.fn()
14
+ const mockCleanup = vi.fn()
15
+
16
+ const mockIsSupported = vi.fn()
17
17
  Object.defineProperty(SpeechRecognitionWrapper, 'isSupported', {
18
18
  get: mockIsSupported,
19
19
  })
@@ -78,7 +78,7 @@ describe('useVocal', () => {
78
78
  current: [, { subscribe }],
79
79
  },
80
80
  } = renderHook(() => useVocal())
81
- subscribe('foo', jest.fn())
81
+ subscribe('foo', vi.fn())
82
82
  expect(mockAddEventListener).not.toHaveBeenCalled()
83
83
  })
84
84
 
@@ -88,7 +88,7 @@ describe('useVocal', () => {
88
88
  current: [, { unsubscribe }],
89
89
  },
90
90
  } = renderHook(() => useVocal())
91
- unsubscribe('foo', jest.fn())
91
+ unsubscribe('foo', vi.fn())
92
92
  expect(mockRemoveEventListener).not.toHaveBeenCalled()
93
93
  })
94
94
  })
@@ -99,7 +99,7 @@ describe('useVocal', () => {
99
99
  })
100
100
 
101
101
  beforeEach(() => {
102
- SpeechRecognitionWrapper.mockImplementation(() => {
102
+ SpeechRecognitionWrapper.mockImplementation(function () {
103
103
  return {
104
104
  start: mockStart,
105
105
  stop: mockStop,
@@ -180,7 +180,7 @@ describe('useVocal', () => {
180
180
  current: [, { subscribe }],
181
181
  },
182
182
  } = renderHook(() => useVocal())
183
- subscribe('foo', jest.fn())
183
+ subscribe('foo', vi.fn())
184
184
  expect(mockAddEventListener).toHaveBeenCalled()
185
185
  })
186
186
 
@@ -190,7 +190,7 @@ describe('useVocal', () => {
190
190
  current: [, { unsubscribe }],
191
191
  },
192
192
  } = renderHook(() => useVocal())
193
- unsubscribe('foo', jest.fn())
193
+ unsubscribe('foo', vi.fn())
194
194
  expect(mockRemoveEventListener).toHaveBeenCalled()
195
195
  })
196
196
  })
@@ -19,5 +19,3 @@ const useTimeout = (handler, timeout = 0) => {
19
19
  }
20
20
 
21
21
  export default useTimeout
22
-
23
- // TODO: Return a promise
@@ -54,5 +54,3 @@ const useVocal = (lang = 'en-US', grammars = null, __rsInstance = null) => {
54
54
  }
55
55
 
56
56
  export default useVocal
57
-
58
- // TODO: Return the instance, not the ref