Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove ability to use findComponent with DOM selector #896

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ test('mounts a component', () => {
})
```

Notice that `mount` accepts a second parameter to define the component's state configuration.
Notice that `mount` accepts a second parameter to define the component's state configuration.

**Example: mounting with component props and a Vue App plugin**
```js
Expand All @@ -58,7 +58,7 @@ const wrapper = mount(Component, {

#### options.global

Among component state, you can configure the aformentioned Vue 3 app by the [`MountingOptions.global` config property.](#global) This would be useful for providing mocked values which your components expect to have available.
Among component state, you can configure the aformentioned Vue 3 app by the [`MountingOptions.global` config property.](#global) This would be useful for providing mocked values which your components expect to have available.

::: tip
If you find yourself having to set common App configuration for many of your tests, then you can set configuration for your entire test suite using the exported [`config` object.](#config)
Expand Down Expand Up @@ -1103,7 +1103,6 @@ findComponent<T extends ComponentPublicInstance>(selector: FindComponentSelector

| syntax | example | details |
| -------------- | ----------------------------- | ------------------------------------------------------------ |
| querySelector | `findComponent('.component')` | Matches standard query selector. |
| Component name | `findComponent({name: 'a'})` | matches PascalCase, snake-case, camelCase |
| Component ref | `findComponent({ref: 'ref'})` | Can be used only on direct ref children of mounted component |
| SFC | `findComponent(Component)` | Pass an imported component directly |
Expand Down
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ interface NameSelector {
name: string
}

export type FindComponentSelector = RefSelector | NameSelector | string
export type FindAllComponentsSelector = NameSelector | string
export type FindComponentSelector = RefSelector | NameSelector
export type FindAllComponentsSelector = NameSelector

export type Slot = VNode | string | { render: Function } | Function | Component

Expand Down
12 changes: 12 additions & 0 deletions src/vueWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ export class VueWrapper<T extends ComponentPublicInstance>
findComponent<T extends ComponentPublicInstance>(
selector: FindComponentSelector | (new () => T)
): VueWrapper<T> {
if (typeof selector === 'string') {
throw Error(
'findComponent requires a Vue constructor or valid find object. If you are searching for DOM nodes, use `find` instead'
)
}

if (typeof selector === 'object' && 'ref' in selector) {
const result = this.vm.$refs[selector.ref]
if (result && !(result instanceof HTMLElement)) {
Expand Down Expand Up @@ -200,6 +206,12 @@ export class VueWrapper<T extends ComponentPublicInstance>
}

findAllComponents(selector: FindAllComponentsSelector): VueWrapper<T>[] {
if (typeof selector === 'string') {
throw Error(
'findAllComponents requires a Vue constructor or valid find object. If you are searching for DOM nodes, use `find` instead'
)
}

return find(this.vm.$.subTree, selector).map((c) => createWrapper(null, c))
}

Expand Down
5 changes: 0 additions & 5 deletions test-dts/getComponent.d-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ const componentByName = wrapper.getComponent({ name: 'ComponentToFind' })
// returns a wrapper with a generic vm (any)
expectType<ComponentPublicInstance>(componentByName.vm)

// get by string
const componentByString = wrapper.getComponent('other')
// returns a wrapper with a generic vm (any)
expectType<ComponentPublicInstance>(componentByString.vm)

// get by ref
const componentByRef = wrapper.getComponent({ ref: 'ref' })
// returns a wrapper with a generic vm (any)
Expand Down
2 changes: 1 addition & 1 deletion tests/attributes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('attributes', () => {
}
})

expect(wrapper.findComponent('.hello-outside').attributes()).toEqual({
expect(wrapper.findComponent({ name: 'Hello' }).attributes()).toEqual({
class: 'hello-outside',
'data-testid': 'hello',
disabled: ''
Expand Down
3 changes: 1 addition & 2 deletions tests/findAllComponents.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ const compA = defineComponent({
describe('findAllComponents', () => {
it('finds all deeply nested vue components', () => {
const wrapper = mount(compA)
// find by DOM selector
expect(wrapper.findAllComponents('.C')).toHaveLength(2)
expect(wrapper.findAllComponents(compC)).toHaveLength(2)
expect(wrapper.findAllComponents({ name: 'Hello' })[0].text()).toBe(
'Hello world'
)
Expand Down
20 changes: 2 additions & 18 deletions tests/findComponent.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ const compA = defineComponent({
describe('findComponent', () => {
it('does not find plain dom elements', () => {
const wrapper = mount(compA)
expect(wrapper.findComponent('.domElement').exists()).toBeFalsy()
// @ts-expect-error
expect(() => wrapper.findComponent('.domElement')).toThrowError()
})

it('finds component by ref', () => {
Expand All @@ -59,23 +60,6 @@ describe('findComponent', () => {
expect(wrapper.findComponent({ ref: 'hello' }).exists()).toBe(false)
})

it('finds component by dom selector', () => {
const wrapper = mount(compA)
// find by DOM selector
expect(wrapper.findComponent('.C').vm).toHaveProperty(
'$options.name',
'ComponentC'
)
})

it('does allows using complicated DOM selector query', () => {
const wrapper = mount(compA)
expect(wrapper.findComponent('.B > .C').vm).toHaveProperty(
'$options.name',
'ComponentC'
)
})

it('finds component by name', () => {
const wrapper = mount(compA)
expect(wrapper.findComponent({ name: 'Hello' }).text()).toBe('Hello world')
Expand Down
13 changes: 7 additions & 6 deletions tests/getComponent.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineComponent } from 'vue'
import { mount, MountingOptions, RouterLinkStub, shallowMount } from '../src'
import { mount, RouterLinkStub, shallowMount } from '../src'
import Issue425 from './components/Issue425.vue'

const compA = defineComponent({
Expand All @@ -15,14 +15,15 @@ describe('getComponent', () => {
it('should delegate to findComponent', () => {
const wrapper = mount(compA)
jest.spyOn(wrapper, 'findComponent').mockReturnThis()
wrapper.getComponent('.domElement')
expect(wrapper.findComponent).toHaveBeenCalledWith('.domElement')
wrapper.getComponent(compA)
expect(wrapper.findComponent).toHaveBeenCalledWith(compA)
})

it('should throw if not found with a string selector', () => {
const wrapper = mount(compA)
// @ts-expect-error
expect(() => wrapper.getComponent('.domElement')).toThrowError(
'Unable to get component with selector .domElement within: <div class="A"></div>'
'findComponent requires a Vue constructor or valid find object. If you are searching for DOM nodes, use `find` instead'
)
})

Expand Down Expand Up @@ -69,12 +70,12 @@ describe('getComponent', () => {
// https://github.com/vuejs/vue-test-utils-next/issues/425
it('works with router-link and mount', () => {
const wrapper = mount(Issue425, options)
expect(wrapper.getComponent('.link').props('to')).toEqual({ name })
expect(wrapper.getComponent(RouterLinkStub).props('to')).toEqual({ name })
})

// https://github.com/vuejs/vue-test-utils-next/issues/425
it('works with router-link and shallowMount', () => {
const wrapper = shallowMount(Issue425, options)
expect(wrapper.getComponent('.link').props('to')).toEqual({ name })
expect(wrapper.getComponent(RouterLinkStub).props('to')).toEqual({ name })
})
})