Skip to content

Commit f26e0d4

Browse files
authored
Merge pull request #75 from cexbrayat/fix/html-element
fix: improve typings of find/get/findAll
2 parents 66d4d44 + 66a68e9 commit f26e0d4

File tree

6 files changed

+149
-14
lines changed

6 files changed

+149
-14
lines changed

src/dom-wrapper.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,47 @@ export class DOMWrapper<ElementType extends Element> implements WrapperAPI {
4141
return this.element.outerHTML
4242
}
4343

44-
find<T extends Element>(selector: string): DOMWrapper<T> | ErrorWrapper {
45-
const result = this.element.querySelector<T>(selector)
44+
find<K extends keyof HTMLElementTagNameMap>(
45+
selector: K
46+
): DOMWrapper<HTMLElementTagNameMap[K]> | ErrorWrapper
47+
find<K extends keyof SVGElementTagNameMap>(
48+
selector: K
49+
): DOMWrapper<SVGElementTagNameMap[K]> | ErrorWrapper
50+
find<T extends Element>(selector: string): DOMWrapper<T> | ErrorWrapper
51+
find(selector: string): DOMWrapper<Element> | ErrorWrapper {
52+
const result = this.element.querySelector(selector)
4653
if (result) {
47-
return new DOMWrapper<T>(result)
54+
return new DOMWrapper(result)
4855
}
4956

5057
return new ErrorWrapper({ selector })
5158
}
5259

53-
get<T extends Element>(selector: string): DOMWrapper<T> {
54-
const result = this.find<T>(selector)
60+
get<K extends keyof HTMLElementTagNameMap>(
61+
selector: K
62+
): DOMWrapper<HTMLElementTagNameMap[K]>
63+
get<K extends keyof SVGElementTagNameMap>(
64+
selector: K
65+
): DOMWrapper<SVGElementTagNameMap[K]>
66+
get<T extends Element>(selector: string): DOMWrapper<T>
67+
get(selector: string): DOMWrapper<Element> {
68+
const result = this.find(selector)
5569
if (result instanceof ErrorWrapper) {
5670
throw new Error(`Unable to find ${selector} within: ${this.html()}`)
5771
}
5872

5973
return result
6074
}
6175

62-
findAll<T extends Element>(selector: string): DOMWrapper<T>[] {
63-
return Array.from(this.element.querySelectorAll<T>(selector)).map(
76+
findAll<K extends keyof HTMLElementTagNameMap>(
77+
selector: K
78+
): DOMWrapper<HTMLElementTagNameMap[K]>[]
79+
findAll<K extends keyof SVGElementTagNameMap>(
80+
selector: K
81+
): DOMWrapper<SVGElementTagNameMap[K]>[]
82+
findAll<T extends Element>(selector: string): DOMWrapper<T>[]
83+
findAll(selector: string): DOMWrapper<Element>[] {
84+
return Array.from(this.element.querySelectorAll(selector)).map(
6485
(x) => new DOMWrapper(x)
6586
)
6687
}

src/error-wrapper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ export class ErrorWrapper {
3636
throw this.wrapperError('find')
3737
}
3838

39+
get(): never {
40+
throw this.wrapperError('get')
41+
}
42+
3943
findAll(): never {
4044
throw this.wrapperError('findAll')
4145
}

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface WrapperAPI {
88
classes: (className?: string) => string[] | boolean | ErrorWrapper
99
readonly element: Element
1010
exists: () => boolean
11+
get<T extends Element>(selector: string): DOMWrapper<T>
1112
find<T extends Element>(selector: string): DOMWrapper<T> | ErrorWrapper
1213
findAll<T extends Element>(selector: string): DOMWrapper<T>[]
1314
html: () => string

src/vue-wrapper.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,32 @@ export class VueWrapper<T extends ComponentPublicInstance>
7878
return this.element.textContent?.trim()
7979
}
8080

81-
find<T extends Element>(selector: string): DOMWrapper<T> | ErrorWrapper {
81+
find<K extends keyof HTMLElementTagNameMap>(
82+
selector: K
83+
): DOMWrapper<HTMLElementTagNameMap[K]> | ErrorWrapper
84+
find<K extends keyof SVGElementTagNameMap>(
85+
selector: K
86+
): DOMWrapper<SVGElementTagNameMap[K]> | ErrorWrapper
87+
find<T extends Element>(selector: string): DOMWrapper<T> | ErrorWrapper
88+
find(selector: string): DOMWrapper<Element> | ErrorWrapper {
8289
// force using the parentElement to allow finding the root element
83-
const result = this.parentElement.querySelector(selector) as T
90+
const result = this.parentElement.querySelector(selector)
8491
if (result) {
8592
return new DOMWrapper(result)
8693
}
8794

8895
return new ErrorWrapper({ selector })
8996
}
9097

91-
get<T extends Element>(selector: string): DOMWrapper<T> {
92-
const result = this.find<T>(selector)
98+
get<K extends keyof HTMLElementTagNameMap>(
99+
selector: K
100+
): DOMWrapper<HTMLElementTagNameMap[K]>
101+
get<K extends keyof SVGElementTagNameMap>(
102+
selector: K
103+
): DOMWrapper<SVGElementTagNameMap[K]>
104+
get<T extends Element>(selector: string): DOMWrapper<T>
105+
get(selector: string): DOMWrapper<Element> {
106+
const result = this.find(selector)
93107
if (result instanceof ErrorWrapper) {
94108
throw new Error(`Unable to find ${selector} within: ${this.html()}`)
95109
}
@@ -110,9 +124,16 @@ export class VueWrapper<T extends ComponentPublicInstance>
110124
return find(this.vm.$.subTree, selector).map((c) => createWrapper(null, c))
111125
}
112126

113-
findAll<T extends Element>(selector: string): DOMWrapper<T>[] {
114-
const results = this.parentElement.querySelectorAll<T>(selector)
115-
return Array.from(results).map((x) => new DOMWrapper(x))
127+
findAll<K extends keyof HTMLElementTagNameMap>(
128+
selector: K
129+
): DOMWrapper<HTMLElementTagNameMap[K]>[]
130+
findAll<K extends keyof SVGElementTagNameMap>(
131+
selector: K
132+
): DOMWrapper<SVGElementTagNameMap[K]>[]
133+
findAll<T extends Element>(selector: string): DOMWrapper<T>[]
134+
findAll(selector: string): DOMWrapper<Element>[] {
135+
const results = this.parentElement.querySelectorAll(selector)
136+
return Array.from(results).map((element) => new DOMWrapper(element))
116137
}
117138

118139
setProps(props: Record<string, any>): Promise<void> {
File renamed without changes.

test-dts/wrapper.d-test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { expectType } from 'tsd'
2+
import { defineComponent } from 'vue'
3+
import { mount } from '../src'
4+
5+
const AppWithDefine = defineComponent({
6+
template: ''
7+
})
8+
9+
const wrapper = mount(AppWithDefine)
10+
const domWrapper = wrapper.find('#other')
11+
12+
// find Vue wrapper
13+
// HTML element selector
14+
let inputMaybe = wrapper.find('input')
15+
expectType<HTMLInputElement | undefined>(inputMaybe.element)
16+
17+
// SVG element selector
18+
let lineMaybe = wrapper.find('line')
19+
expectType<SVGLineElement | undefined>(lineMaybe.element)
20+
21+
// string selector
22+
let byClassMaybe = wrapper.find('.todo')
23+
expectType<Element | undefined>(byClassMaybe.element)
24+
25+
// find DOM wrapper
26+
// HTML element selector
27+
inputMaybe = domWrapper.find('input')
28+
expectType<HTMLInputElement | undefined>(inputMaybe.element)
29+
30+
// SVG element selector
31+
lineMaybe = domWrapper.find('line')
32+
expectType<SVGLineElement | undefined>(lineMaybe.element)
33+
34+
// string selector
35+
byClassMaybe = domWrapper.find('.todo')
36+
expectType<Element | undefined>(byClassMaybe.element)
37+
38+
// findAll
39+
// HTML element selector
40+
let inputArray = wrapper.findAll('input')
41+
expectType<HTMLInputElement | undefined>(inputArray[0].element)
42+
43+
// SVG element selector
44+
let lineArray = wrapper.findAll('line')
45+
expectType<SVGLineElement | undefined>(lineArray[0].element)
46+
47+
// string selector
48+
let byClassArray = wrapper.findAll('.todo')
49+
expectType<Element | undefined>(byClassArray[0].element)
50+
51+
// findAll DOM wrapper
52+
// HTML element selector
53+
inputArray = domWrapper.findAll('input')
54+
expectType<HTMLInputElement | undefined>(inputArray[0].element)
55+
56+
// SVG element selector
57+
lineArray = domWrapper.findAll('line')
58+
expectType<SVGLineElement | undefined>(lineArray[0].element)
59+
60+
// string selector
61+
byClassArray = domWrapper.findAll('.todo')
62+
expectType<Element | undefined>(byClassArray[0].element)
63+
64+
// get
65+
// HTML element selector
66+
let input = wrapper.get('input')
67+
expectType<HTMLInputElement>(input.element)
68+
69+
// SVG element selector
70+
let line = wrapper.get('line')
71+
expectType<SVGLineElement>(line.element)
72+
73+
// string selector
74+
let byClass = wrapper.get('.todo')
75+
expectType<Element>(byClass.element)
76+
77+
// get DOM wrapper
78+
// HTML element selector
79+
input = domWrapper.get('input')
80+
expectType<HTMLInputElement>(input.element)
81+
82+
// SVG element selector
83+
line = domWrapper.get('line')
84+
expectType<SVGLineElement>(line.element)
85+
86+
// string selector
87+
byClass = domWrapper.get('.todo')
88+
expectType<Element>(byClass.element)

0 commit comments

Comments
 (0)