preact-missing-hooks 4.8.0 → 4.9.0
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.
- package/Readme.md +88 -34
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.modern.mjs +1 -1
- package/dist/index.modern.mjs.map +1 -1
- package/dist/index.module.js +1 -1
- package/dist/index.module.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/react.js +153 -6
- package/dist/useDeviceData.d.ts +26 -2
- package/docs/README.md +1 -1
- package/docs/main.js +25 -7
- package/package.json +1 -1
- package/src/useDeviceData.ts +199 -6
- package/tests/useDeviceData.test.tsx +129 -26
|
@@ -1,7 +1,46 @@
|
|
|
1
1
|
/** @jsx h */
|
|
2
2
|
import { h } from 'preact'
|
|
3
3
|
import { render, waitFor } from '@testing-library/preact'
|
|
4
|
-
import { getDeviceData, useDeviceData } from '../src/useDeviceData'
|
|
4
|
+
import { getDeviceData, parseUserAgent, useDeviceData } from '../src/useDeviceData'
|
|
5
|
+
|
|
6
|
+
const CHROME_WIN_UA =
|
|
7
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
|
8
|
+
|
|
9
|
+
describe('parseUserAgent', () => {
|
|
10
|
+
it('parses Chrome on Windows', () => {
|
|
11
|
+
const parsed = parseUserAgent(CHROME_WIN_UA)
|
|
12
|
+
expect(parsed.browser).toEqual({ name: 'Chrome', version: '120.0.0.0' })
|
|
13
|
+
expect(parsed.os).toEqual({ name: 'Windows', version: '10.0' })
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('parses Firefox on macOS', () => {
|
|
17
|
+
const ua =
|
|
18
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0'
|
|
19
|
+
const parsed = parseUserAgent(ua)
|
|
20
|
+
expect(parsed.browser.name).toBe('Firefox')
|
|
21
|
+
expect(parsed.browser.version).toBe('121.0')
|
|
22
|
+
expect(parsed.os.name).toBe('macOS')
|
|
23
|
+
expect(parsed.os.version).toBe('10.15')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('parses Safari on iOS', () => {
|
|
27
|
+
const ua =
|
|
28
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1'
|
|
29
|
+
const parsed = parseUserAgent(ua)
|
|
30
|
+
expect(parsed.browser.name).toBe('Safari')
|
|
31
|
+
expect(parsed.browser.version).toBe('17.2')
|
|
32
|
+
expect(parsed.os.name).toBe('iOS')
|
|
33
|
+
expect(parsed.os.version).toBe('17.2')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('parses Edge from user-agent', () => {
|
|
37
|
+
const ua =
|
|
38
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0'
|
|
39
|
+
const parsed = parseUserAgent(ua)
|
|
40
|
+
expect(parsed.browser.name).toBe('Edge')
|
|
41
|
+
expect(parsed.browser.version).toBe('120.0.0.0')
|
|
42
|
+
})
|
|
43
|
+
})
|
|
5
44
|
|
|
6
45
|
describe('getDeviceData', () => {
|
|
7
46
|
const originalNavigator = global.navigator
|
|
@@ -25,14 +64,16 @@ describe('getDeviceData', () => {
|
|
|
25
64
|
const data = getDeviceData()
|
|
26
65
|
vi.unstubAllGlobals()
|
|
27
66
|
expect(data.userAgent).toBe('')
|
|
67
|
+
expect(data.browser).toEqual({ name: 'Unknown', version: '' })
|
|
68
|
+
expect(data.os).toEqual({ name: 'Unknown', version: '' })
|
|
28
69
|
expect(data.online).toBe(true)
|
|
29
70
|
expect(data.viewport.width).toBe(0)
|
|
30
71
|
})
|
|
31
72
|
|
|
32
|
-
it('reads navigator and screen fields', () => {
|
|
73
|
+
it('reads navigator, browser, OS, and screen fields', () => {
|
|
33
74
|
Object.defineProperty(global, 'navigator', {
|
|
34
75
|
value: {
|
|
35
|
-
userAgent:
|
|
76
|
+
userAgent: CHROME_WIN_UA,
|
|
36
77
|
language: 'en-US',
|
|
37
78
|
languages: ['en-US', 'en'],
|
|
38
79
|
platform: 'Win32',
|
|
@@ -57,25 +98,31 @@ describe('getDeviceData', () => {
|
|
|
57
98
|
})
|
|
58
99
|
|
|
59
100
|
const data = getDeviceData()
|
|
60
|
-
expect(data.userAgent).toBe(
|
|
101
|
+
expect(data.userAgent).toBe(CHROME_WIN_UA)
|
|
102
|
+
expect(data.browser.name).toBe('Chrome')
|
|
103
|
+
expect(data.browser.version).toBe('120.0.0.0')
|
|
104
|
+
expect(data.os.name).toBe('Windows')
|
|
105
|
+
expect(data.os.version).toBe('10.0')
|
|
61
106
|
expect(data.language).toBe('en-US')
|
|
62
|
-
expect(data.languages).toEqual(['en-US', 'en'])
|
|
63
107
|
expect(data.platform).toBe('Win32')
|
|
64
108
|
expect(data.hardwareConcurrency).toBe(8)
|
|
65
|
-
expect(data.deviceMemory).toBe(8)
|
|
66
109
|
expect(data.screen.width).toBe(1920)
|
|
67
|
-
expect(data.screen.height).toBe(1080)
|
|
68
110
|
expect(data.touch).toBe(false)
|
|
69
111
|
})
|
|
70
112
|
|
|
71
|
-
it('
|
|
113
|
+
it('prefers Client Hints brands over user-agent for browser name', () => {
|
|
72
114
|
Object.defineProperty(global, 'navigator', {
|
|
73
115
|
value: {
|
|
74
116
|
...originalNavigator,
|
|
117
|
+
userAgent:
|
|
118
|
+
'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 Chrome/120.0.0.0',
|
|
75
119
|
userAgentData: {
|
|
76
120
|
mobile: true,
|
|
77
121
|
platform: 'Android',
|
|
78
|
-
brands: [
|
|
122
|
+
brands: [
|
|
123
|
+
{ brand: 'Not A Brand', version: '99' },
|
|
124
|
+
{ brand: 'Google Chrome', version: '120.0.0.0' },
|
|
125
|
+
],
|
|
79
126
|
},
|
|
80
127
|
},
|
|
81
128
|
writable: true,
|
|
@@ -83,8 +130,9 @@ describe('getDeviceData', () => {
|
|
|
83
130
|
|
|
84
131
|
const data = getDeviceData()
|
|
85
132
|
expect(data.userAgentData?.mobile).toBe(true)
|
|
86
|
-
expect(data.
|
|
87
|
-
expect(data.
|
|
133
|
+
expect(data.browser.name).toBe('Google Chrome')
|
|
134
|
+
expect(data.browser.version).toBe('120.0.0.0')
|
|
135
|
+
expect(data.os.name).toBe('Android')
|
|
88
136
|
})
|
|
89
137
|
})
|
|
90
138
|
|
|
@@ -102,37 +150,89 @@ describe('useDeviceData', () => {
|
|
|
102
150
|
window.removeEventListener = originalRemoveEventListener
|
|
103
151
|
})
|
|
104
152
|
|
|
105
|
-
it('returns
|
|
153
|
+
it('returns browser and OS from navigator user-agent', () => {
|
|
106
154
|
Object.defineProperty(global, 'navigator', {
|
|
107
155
|
value: {
|
|
108
156
|
...originalNavigator,
|
|
109
|
-
userAgent:
|
|
157
|
+
userAgent: CHROME_WIN_UA,
|
|
110
158
|
language: 'fr',
|
|
111
159
|
languages: ['fr'],
|
|
112
|
-
platform: '
|
|
160
|
+
platform: 'Win32',
|
|
113
161
|
cookieEnabled: true,
|
|
114
162
|
onLine: true,
|
|
115
|
-
maxTouchPoints:
|
|
116
|
-
vendor: '
|
|
163
|
+
maxTouchPoints: 0,
|
|
164
|
+
vendor: 'Google Inc.',
|
|
117
165
|
},
|
|
118
166
|
writable: true,
|
|
119
167
|
})
|
|
120
168
|
|
|
121
169
|
function TestComponent() {
|
|
122
|
-
const device = useDeviceData({
|
|
170
|
+
const device = useDeviceData({
|
|
171
|
+
includeBattery: false,
|
|
172
|
+
includeHighEntropy: false,
|
|
173
|
+
})
|
|
123
174
|
return (
|
|
124
175
|
<div>
|
|
125
|
-
<span data-testid="
|
|
176
|
+
<span data-testid="browser">
|
|
177
|
+
{device.browser.name}:{device.browser.version}
|
|
178
|
+
</span>
|
|
179
|
+
<span data-testid="os">
|
|
180
|
+
{device.os.name}:{device.os.version}
|
|
181
|
+
</span>
|
|
126
182
|
<span data-testid="lang">{device.language}</span>
|
|
127
|
-
<span data-testid="touch">{String(device.touch)}</span>
|
|
128
183
|
</div>
|
|
129
184
|
)
|
|
130
185
|
}
|
|
131
186
|
|
|
132
187
|
const { getByTestId } = render(<TestComponent />)
|
|
133
|
-
expect(getByTestId('
|
|
188
|
+
expect(getByTestId('browser').textContent).toBe('Chrome:120.0.0.0')
|
|
189
|
+
expect(getByTestId('os').textContent).toBe('Windows:10.0')
|
|
134
190
|
expect(getByTestId('lang').textContent).toBe('fr')
|
|
135
|
-
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('enriches browser and OS version from getHighEntropyValues', async () => {
|
|
194
|
+
Object.defineProperty(global, 'navigator', {
|
|
195
|
+
value: {
|
|
196
|
+
...originalNavigator,
|
|
197
|
+
userAgent: CHROME_WIN_UA,
|
|
198
|
+
userAgentData: {
|
|
199
|
+
platform: 'Windows',
|
|
200
|
+
brands: [{ brand: 'Google Chrome', version: '120.0.0.0' }],
|
|
201
|
+
getHighEntropyValues: vi.fn().mockResolvedValue({
|
|
202
|
+
platformVersion: '15.0.0',
|
|
203
|
+
fullVersionList: [
|
|
204
|
+
{ brand: 'Google Chrome', version: '120.0.6099.130' },
|
|
205
|
+
],
|
|
206
|
+
}),
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
writable: true,
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
function TestComponent() {
|
|
213
|
+
const device = useDeviceData({
|
|
214
|
+
includeBattery: false,
|
|
215
|
+
includeHighEntropy: true,
|
|
216
|
+
})
|
|
217
|
+
return (
|
|
218
|
+
<div>
|
|
219
|
+
<span data-testid="browser">
|
|
220
|
+
{device.browser.name}:{device.browser.version}
|
|
221
|
+
</span>
|
|
222
|
+
<span data-testid="os">
|
|
223
|
+
{device.os.name}:{device.os.version}
|
|
224
|
+
</span>
|
|
225
|
+
</div>
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const { getByTestId } = render(<TestComponent />)
|
|
230
|
+
await waitFor(() => {
|
|
231
|
+
expect(getByTestId('browser').textContent).toBe(
|
|
232
|
+
'Google Chrome:120.0.6099.130',
|
|
233
|
+
)
|
|
234
|
+
expect(getByTestId('os').textContent).toBe('Windows:15.0.0')
|
|
235
|
+
})
|
|
136
236
|
})
|
|
137
237
|
|
|
138
238
|
it('updates viewport on resize', async () => {
|
|
@@ -152,15 +252,16 @@ describe('useDeviceData', () => {
|
|
|
152
252
|
})
|
|
153
253
|
|
|
154
254
|
Object.defineProperty(global, 'navigator', {
|
|
155
|
-
value: { ...originalNavigator, onLine: true },
|
|
255
|
+
value: { ...originalNavigator, onLine: true, userAgent: CHROME_WIN_UA },
|
|
156
256
|
writable: true,
|
|
157
257
|
})
|
|
158
258
|
|
|
159
259
|
function TestComponent() {
|
|
160
|
-
const device = useDeviceData({
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
)
|
|
260
|
+
const device = useDeviceData({
|
|
261
|
+
includeBattery: false,
|
|
262
|
+
includeHighEntropy: false,
|
|
263
|
+
})
|
|
264
|
+
return <span data-testid="vw">{device.viewport.width}</span>
|
|
164
265
|
}
|
|
165
266
|
|
|
166
267
|
const { getByTestId } = render(<TestComponent />)
|
|
@@ -182,6 +283,7 @@ describe('useDeviceData', () => {
|
|
|
182
283
|
value: {
|
|
183
284
|
...originalNavigator,
|
|
184
285
|
onLine: true,
|
|
286
|
+
userAgent: CHROME_WIN_UA,
|
|
185
287
|
getBattery: () =>
|
|
186
288
|
Promise.resolve({ charging: true, level: 0.75 }),
|
|
187
289
|
},
|
|
@@ -191,6 +293,7 @@ describe('useDeviceData', () => {
|
|
|
191
293
|
function TestComponent() {
|
|
192
294
|
const device = useDeviceData({
|
|
193
295
|
includeBattery: true,
|
|
296
|
+
includeHighEntropy: false,
|
|
194
297
|
batteryPollIntervalMs: 0,
|
|
195
298
|
})
|
|
196
299
|
return (
|