Skip to content

Commit 8e94427

Browse files
authoredDec 9, 2024··
fix(browser): fix user event state on preview provider (#7041)
1 parent 2b5c520 commit 8e94427

File tree

3 files changed

+130
-66
lines changed

3 files changed

+130
-66
lines changed
 

‎packages/browser/src/client/tester/context.ts

+101-21
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,20 @@ function triggerCommand<T>(command: string, ...args: any[]) {
3030
}
3131

3232
export function createUserEvent(__tl_user_event_base__?: TestingLibraryUserEvent, options?: TestingLibraryOptions): UserEvent {
33-
let __tl_user_event__ = __tl_user_event_base__?.setup(options ?? {})
33+
if (__tl_user_event_base__) {
34+
return createPreviewUserEvent(__tl_user_event_base__, options ?? {})
35+
}
36+
3437
const keyboard = {
3538
unreleased: [] as string[],
3639
}
3740

3841
return {
39-
setup(options?: any) {
40-
return createUserEvent(__tl_user_event_base__, options)
42+
setup() {
43+
return createUserEvent()
4144
},
4245
async cleanup() {
4346
return ensureAwaited(async () => {
44-
if (typeof __tl_user_event_base__ !== 'undefined') {
45-
__tl_user_event__ = __tl_user_event_base__?.setup(options ?? {})
46-
return
47-
}
4847
await triggerCommand('__vitest_cleanup', keyboard)
4948
keyboard.unreleased = []
5049
})
@@ -87,14 +86,6 @@ export function createUserEvent(__tl_user_event_base__?: TestingLibraryUserEvent
8786
// testing-library user-event
8887
async type(element: Element | Locator, text: string, options: UserEventTypeOptions = {}) {
8988
return ensureAwaited(async () => {
90-
if (typeof __tl_user_event__ !== 'undefined') {
91-
return __tl_user_event__.type(
92-
element instanceof Element ? element : element.element(),
93-
text,
94-
options,
95-
)
96-
}
97-
9889
const selector = convertToSelector(element)
9990
const { unreleased } = await triggerCommand<{ unreleased: string[] }>(
10091
'__vitest_type',
@@ -107,17 +98,11 @@ export function createUserEvent(__tl_user_event_base__?: TestingLibraryUserEvent
10798
},
10899
tab(options: UserEventTabOptions = {}) {
109100
return ensureAwaited(() => {
110-
if (typeof __tl_user_event__ !== 'undefined') {
111-
return __tl_user_event__.tab(options)
112-
}
113101
return triggerCommand('__vitest_tab', options)
114102
})
115103
},
116104
async keyboard(text: string) {
117105
return ensureAwaited(async () => {
118-
if (typeof __tl_user_event__ !== 'undefined') {
119-
return __tl_user_event__.keyboard(text)
120-
}
121106
const { unreleased } = await triggerCommand<{ unreleased: string[] }>(
122107
'__vitest_keyboard',
123108
text,
@@ -129,6 +114,101 @@ export function createUserEvent(__tl_user_event_base__?: TestingLibraryUserEvent
129114
}
130115
}
131116

117+
function createPreviewUserEvent(userEventBase: TestingLibraryUserEvent, options: TestingLibraryOptions): UserEvent {
118+
let userEvent = userEventBase.setup(options)
119+
120+
function toElement(element: Element | Locator) {
121+
return element instanceof Element ? element : element.element()
122+
}
123+
124+
const vitestUserEvent: UserEvent = {
125+
setup(options?: any) {
126+
return createPreviewUserEvent(userEventBase, options)
127+
},
128+
async cleanup() {
129+
userEvent = userEventBase.setup(options ?? {})
130+
},
131+
async click(element) {
132+
await userEvent.click(toElement(element))
133+
},
134+
async dblClick(element) {
135+
await userEvent.dblClick(toElement(element))
136+
},
137+
async tripleClick(element) {
138+
await userEvent.tripleClick(toElement(element))
139+
},
140+
async selectOptions(element, value) {
141+
const options = (Array.isArray(value) ? value : [value]).map((option) => {
142+
if (typeof option !== 'string') {
143+
return toElement(option)
144+
}
145+
return option
146+
})
147+
await userEvent.selectOptions(
148+
element,
149+
options as string[] | HTMLElement[],
150+
)
151+
},
152+
async clear(element) {
153+
await userEvent.clear(toElement(element))
154+
},
155+
async hover(element: Element | Locator) {
156+
await userEvent.hover(toElement(element))
157+
},
158+
async unhover(element: Element | Locator) {
159+
await userEvent.unhover(toElement(element))
160+
},
161+
async upload(element: Element | Locator, files: string | string[] | File | File[]) {
162+
const uploadPromise = (Array.isArray(files) ? files : [files]).map(async (file) => {
163+
if (typeof file !== 'string') {
164+
return file
165+
}
166+
167+
const { content: base64, basename, mime } = await triggerCommand<{
168+
content: string
169+
basename: string
170+
mime: string
171+
}>('__vitest_fileInfo', file, 'base64')
172+
173+
const fileInstance = fetch(`data:${mime};base64,${base64}`)
174+
.then(r => r.blob())
175+
.then(blob => new File([blob], basename, { type: mime }))
176+
return fileInstance
177+
})
178+
const uploadFiles = await Promise.all(uploadPromise)
179+
return userEvent.upload(toElement(element) as HTMLElement, uploadFiles)
180+
},
181+
182+
async fill(element: Element | Locator, text: string) {
183+
await userEvent.clear(toElement(element))
184+
return userEvent.type(toElement(element), text)
185+
},
186+
async dragAndDrop() {
187+
throw new Error(`The "preview" provider doesn't support 'userEvent.dragAndDrop'`)
188+
},
189+
190+
async type(element: Element | Locator, text: string, options: UserEventTypeOptions = {}) {
191+
await userEvent.type(toElement(element), text, options)
192+
},
193+
async tab(options: UserEventTabOptions = {}) {
194+
await userEvent.tab(options)
195+
},
196+
async keyboard(text: string) {
197+
await userEvent.keyboard(text)
198+
},
199+
}
200+
201+
for (const [name, fn] of Object.entries(vitestUserEvent)) {
202+
if (name !== 'setup') {
203+
(vitestUserEvent as any)[name] = function (this: any, ...args: any[]) {
204+
return ensureAwaited(() => fn.apply(this, args))
205+
}
206+
}
207+
}
208+
209+
return vitestUserEvent
210+
}
211+
132212
export function cdp() {
133213
return getBrowserState().cdp!
134214
}

‎packages/browser/src/client/tester/locators/preview.ts

+11-44
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { userEvent } from '@testing-library/user-event'
2-
import { page, server } from '@vitest/browser/context'
1+
import { page, server, userEvent } from '@vitest/browser/context'
32
import {
43
getByAltTextSelector,
54
getByLabelSelector,
@@ -9,7 +8,7 @@ import {
98
getByTextSelector,
109
getByTitleSelector,
1110
} from 'ivya'
12-
import { convertElementToCssSelector, ensureAwaited } from '../../utils'
11+
import { convertElementToCssSelector } from '../../utils'
1312
import { getElementError } from '../public-utils'
1413
import { Locator, selectorEngine } from './index'
1514

@@ -58,71 +57,39 @@ class PreviewLocator extends Locator {
5857
}
5958

6059
click(): Promise<void> {
61-
return ensureAwaited(() => userEvent.click(this.element()))
60+
return userEvent.click(this.element())
6261
}
6362

6463
dblClick(): Promise<void> {
65-
return ensureAwaited(() => userEvent.dblClick(this.element()))
64+
return userEvent.dblClick(this.element())
6665
}
6766

6867
tripleClick(): Promise<void> {
69-
return ensureAwaited(() => userEvent.tripleClick(this.element()))
68+
return userEvent.tripleClick(this.element())
7069
}
7170

7271
hover(): Promise<void> {
73-
return ensureAwaited(() => userEvent.hover(this.element()))
72+
return userEvent.hover(this.element())
7473
}
7574

7675
unhover(): Promise<void> {
77-
return ensureAwaited(() => userEvent.unhover(this.element()))
76+
return userEvent.unhover(this.element())
7877
}
7978

8079
async fill(text: string): Promise<void> {
81-
await this.clear()
82-
return ensureAwaited(() => userEvent.type(this.element(), text))
80+
return userEvent.fill(this.element(), text)
8381
}
8482

8583
async upload(file: string | string[] | File | File[]): Promise<void> {
86-
const uploadPromise = (Array.isArray(file) ? file : [file]).map(async (file) => {
87-
if (typeof file !== 'string') {
88-
return file
89-
}
90-
91-
const { content: base64, basename, mime } = await this.triggerCommand<{
92-
content: string
93-
basename: string
94-
mime: string
95-
}>('__vitest_fileInfo', file, 'base64')
96-
97-
const fileInstance = fetch(`data:${mime};base64,${base64}`)
98-
.then(r => r.blob())
99-
.then(blob => new File([blob], basename, { type: mime }))
100-
return fileInstance
101-
})
102-
const uploadFiles = await Promise.all(uploadPromise)
103-
return ensureAwaited(() => userEvent.upload(this.element() as HTMLElement, uploadFiles))
84+
return userEvent.upload(this.element(), file)
10485
}
10586

10687
selectOptions(options_: string | string[] | HTMLElement | HTMLElement[] | Locator | Locator[]): Promise<void> {
107-
const options = (Array.isArray(options_) ? options_ : [options_]).map((option) => {
108-
if (typeof option !== 'string' && 'element' in option) {
109-
return option.element() as HTMLElement
110-
}
111-
return option
112-
})
113-
return ensureAwaited(() => userEvent.selectOptions(this.element(), options as string[] | HTMLElement[]))
114-
}
115-
116-
async dropTo(): Promise<void> {
117-
throw new Error('The "preview" provider doesn\'t support `dropTo` method.')
88+
return userEvent.selectOptions(this.element(), options_)
11889
}
11990

12091
clear(): Promise<void> {
121-
return ensureAwaited(() => userEvent.clear(this.element()))
122-
}
123-
124-
async screenshot(): Promise<never> {
125-
throw new Error('The "preview" provider doesn\'t support `screenshot` method.')
92+
return userEvent.clear(this.element())
12693
}
12794

12895
protected locator(selector: string) {

‎test/browser/fixtures/user-event/keyboard.test.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect, test } from 'vitest'
2-
import { userEvent, page, server } from '@vitest/browser/context'
2+
import { userEvent, page } from '@vitest/browser/context'
33

44
test('non US keys', async () => {
55
document.body.innerHTML = `
@@ -34,3 +34,20 @@ test('non US keys', async () => {
3434
console.error(e)
3535
}
3636
})
37+
38+
test('click with modifier', async () => {
39+
document.body.innerHTML = `
40+
<div id="test">test shift and click</div>
41+
`
42+
const el = document.getElementById("test")
43+
el.addEventListener("pointerup", (e) => {
44+
if (e.shiftKey && e.type === 'pointerup') {
45+
el.textContent += " [ok]"
46+
}
47+
});
48+
49+
await userEvent.keyboard('{Shift>}')
50+
await userEvent.click(el)
51+
await userEvent.keyboard('{/Shift}')
52+
await expect.poll(() => el.textContent).toContain("[ok]")
53+
})

0 commit comments

Comments
 (0)
Please sign in to comment.