-
Notifications
You must be signed in to change notification settings - Fork 248
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
feat: split attrs and props mounting options #99
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,21 +7,25 @@ const AppWithDefine = defineComponent({ | |
a: { | ||
type: String, | ||
required: true | ||
} | ||
}, | ||
b: Number | ||
}, | ||
template: '' | ||
}) | ||
|
||
// accept props | ||
let wrapper = mount(AppWithDefine, { | ||
props: { a: 'Hello' } | ||
props: { a: 'Hello', b: 2 } | ||
}) | ||
// vm is properly typed | ||
expectType<string>(wrapper.vm.a) | ||
|
||
// can receive extra props | ||
// ideally, it should not | ||
// but the props have type { a: string } & VNodeProps | ||
// which allows any property | ||
mount(AppWithDefine, { | ||
props: { a: 'Hello', b: 2 } | ||
props: { a: 'Hello', c: 2 } | ||
}) | ||
|
||
// wrong prop type should not compile | ||
|
@@ -48,10 +52,12 @@ wrapper = mount(AppWithProps, { | |
// vm is properly typed | ||
expectType<string>(wrapper.vm.a) | ||
|
||
// can receive extra props | ||
mount(AppWithProps, { | ||
props: { a: 'Hello', b: 2 } | ||
}) | ||
// can't receive extra props | ||
expectError( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this error out for transparent components without There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you give me an example of what you mean? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a component has no props defined, and passes them down via const Component = {
template: `<div><ChildComponent v-bind="$attrs"/></div>`
}
const wrapper = mount(Component, { props: { a:'a' } })
expect(wrapper.findComponent(ChildComponent).props('a')).toBe('a') There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so yes: I expect this to error out if the props are not defined (as they end up with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This wont error out for plain JS use right? Erroring out is technically wrong, as the Vue API allows you to pass props to that kind of components, but as long as we state the reasoning behind this, should be fine :) You are right, its not a common use case and having type safety for the 80% of people is better, we just need to make sure these little cases have workarounds for the more novice TS users out there. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes this is for TS developers only, JS will be fine, sorry if that wasn't clear :) And even in TS it still works, it just forces the developers to cast the IMO, TS developers will be happy to have intellisense/autocomplete/type-checking in tests, and won't be too lost if the compilation indicates that the props does not match the props of the component. But you're 100% right that we need to mention it the docs 👍 |
||
mount(AppWithProps, { | ||
props: { a: 'Hello', b: 2 } | ||
}) | ||
) | ||
|
||
// wrong prop type should not compile | ||
expectError( | ||
|
@@ -73,6 +79,7 @@ wrapper = mount(AppWithArrayProps, { | |
expectType<string>(wrapper.vm.a) | ||
|
||
// can receive extra props | ||
// as they are declared as `string[]` | ||
mount(AppWithArrayProps, { | ||
props: { a: 'Hello', b: 2 } | ||
}) | ||
|
@@ -81,7 +88,9 @@ const AppWithoutProps = { | |
template: '' | ||
} | ||
|
||
// can receive extra props | ||
wrapper = mount(AppWithoutProps, { | ||
props: { b: 'Hello' } | ||
}) | ||
// can't receive extra props | ||
expectError( | ||
(wrapper = mount(AppWithoutProps, { | ||
props: { b: 'Hello' } | ||
})) | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { defineComponent, h } from 'vue' | ||
import { mount } from '../../src' | ||
|
||
describe('mountingOptions.attrs', () => { | ||
const Component = defineComponent({ | ||
props: { | ||
message: { | ||
type: String, | ||
required: true | ||
}, | ||
otherMessage: { | ||
type: String | ||
} | ||
}, | ||
|
||
render() { | ||
return h('div', {}, `Message is ${this.message}`) | ||
} | ||
}) | ||
|
||
test('assigns extra attributes on components', () => { | ||
const wrapper = mount(Component, { | ||
props: { | ||
message: 'Hello World' | ||
}, | ||
attrs: { | ||
class: 'HelloFromTheOtherSide', | ||
id: 'hello', | ||
disabled: true | ||
} | ||
}) | ||
|
||
expect(wrapper.attributes()).toEqual({ | ||
class: 'HelloFromTheOtherSide', | ||
disabled: 'true', | ||
id: 'hello' | ||
}) | ||
|
||
expect(wrapper.props()).toEqual({ | ||
message: 'Hello World' | ||
}) | ||
}) | ||
|
||
test('assigns event listeners', async () => { | ||
const Component = { | ||
template: '<button @click="$emit(\'customEvent\', true)">Click</button>' | ||
} | ||
const onCustomEvent = jest.fn() | ||
const wrapper = mount(Component, { attrs: { onCustomEvent } }) | ||
const button = wrapper.find('button') | ||
await button.trigger('click') | ||
await button.trigger('click') | ||
await button.trigger('click') | ||
|
||
expect(onCustomEvent).toHaveBeenCalledTimes(3) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,23 @@ | ||
import { defineComponent, h } from 'vue' | ||
import WithProps from '../components/WithProps.vue' | ||
import { mount } from '../../src' | ||
|
||
describe('mountingOptions.props', () => { | ||
test('passes props', () => { | ||
const Component = defineComponent({ | ||
props: { | ||
message: { | ||
type: String, | ||
required: true | ||
} | ||
const Component = defineComponent({ | ||
props: { | ||
message: { | ||
type: String, | ||
required: true | ||
}, | ||
|
||
render() { | ||
return h('div', {}, `Message is ${this.message}`) | ||
otherMessage: { | ||
type: String | ||
} | ||
}) | ||
}, | ||
|
||
render() { | ||
return h('div', {}, `Message is ${this.message}`) | ||
} | ||
}) | ||
test('passes props', () => { | ||
const wrapper = mount(Component, { | ||
props: { | ||
message: 'Hello' | ||
|
@@ -25,38 +26,27 @@ describe('mountingOptions.props', () => { | |
expect(wrapper.text()).toBe('Message is Hello') | ||
}) | ||
|
||
test('assigns extra attributes on components', () => { | ||
const wrapper = mount(WithProps, { | ||
test('assigns extra properties as attributes on components', () => { | ||
// the recommended way is to use `attrs` though | ||
// and ideally it should not even compile, but props is too loosely typed | ||
// for components defined with `defineComponent` | ||
const wrapper = mount(Component, { | ||
props: { | ||
message: 'Hello World', | ||
class: 'HelloFromTheOtherSide', | ||
id: 'hello', | ||
disabled: true, | ||
msg: 'Hello World' | ||
disabled: true | ||
} | ||
}) | ||
|
||
expect(wrapper.props()).toEqual({ | ||
message: 'Hello World' | ||
}) | ||
|
||
expect(wrapper.attributes()).toEqual({ | ||
class: 'HelloFromTheOtherSide', | ||
disabled: 'true', | ||
id: 'hello' | ||
}) | ||
|
||
expect(wrapper.props()).toEqual({ | ||
msg: 'Hello World' | ||
}) | ||
}) | ||
|
||
test('assigns event listeners', async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is technically the right way to attach a listener, not through attrs, even though they are identical as of now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So your point is that we should keep the possibility of assigning extra props for this use-case?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So extra props would be attrs basically? If so, then I say just mention in the docs that people can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we can nudge the developers in the direction we feel makes more sense in the docs, either:
|
||
const Component = { | ||
template: '<button @click="$emit(\'customEvent\', true)">Click</button>' | ||
} | ||
const onCustomEvent = jest.fn() | ||
const wrapper = mount(Component, { props: { onCustomEvent } }) | ||
const button = wrapper.find('button') | ||
await button.trigger('click') | ||
await button.trigger('click') | ||
await button.trigger('click') | ||
|
||
expect(onCustomEvent).toHaveBeenCalledTimes(3) | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use this: