componentProps.spec.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. /**
  2. * @vitest-environment jsdom
  3. */
  4. import {
  5. type ComponentInternalInstance,
  6. type FunctionalComponent,
  7. type SetupContext,
  8. createApp,
  9. defineComponent,
  10. getCurrentInstance,
  11. h,
  12. inject,
  13. nextTick,
  14. nodeOps,
  15. provide,
  16. ref,
  17. render,
  18. serializeInner,
  19. toRefs,
  20. watch,
  21. } from '@vue/runtime-test'
  22. import { render as domRender } from 'vue'
  23. describe('component props', () => {
  24. test('stateful', () => {
  25. let props: any
  26. let attrs: any
  27. let proxy: any
  28. const Comp = defineComponent({
  29. props: ['fooBar', 'barBaz'],
  30. render() {
  31. props = this.$props
  32. attrs = this.$attrs
  33. proxy = this
  34. },
  35. })
  36. const root = nodeOps.createElement('div')
  37. render(h(Comp, { fooBar: 1, bar: 2 }), root)
  38. expect(proxy.fooBar).toBe(1)
  39. expect(props).toEqual({ fooBar: 1 })
  40. expect(attrs).toEqual({ bar: 2 })
  41. // test passing kebab-case and resolving to camelCase
  42. render(h(Comp, { 'foo-bar': 2, bar: 3, baz: 4 }), root)
  43. expect(proxy.fooBar).toBe(2)
  44. expect(props).toEqual({ fooBar: 2 })
  45. expect(attrs).toEqual({ bar: 3, baz: 4 })
  46. // test updating kebab-case should not delete it (#955)
  47. render(h(Comp, { 'foo-bar': 3, bar: 3, baz: 4, barBaz: 5 }), root)
  48. expect(proxy.fooBar).toBe(3)
  49. expect(proxy.barBaz).toBe(5)
  50. expect(props).toEqual({ fooBar: 3, barBaz: 5 })
  51. expect(attrs).toEqual({ bar: 3, baz: 4 })
  52. render(h(Comp, { qux: 5 }), root)
  53. expect(proxy.fooBar).toBeUndefined()
  54. // remove the props with camelCase key (#1412)
  55. expect(proxy.barBaz).toBeUndefined()
  56. expect(props).toEqual({})
  57. expect(attrs).toEqual({ qux: 5 })
  58. })
  59. test('stateful with setup', () => {
  60. let props: any
  61. let attrs: any
  62. const Comp = defineComponent({
  63. props: ['foo'],
  64. setup(_props, { attrs: _attrs }) {
  65. return () => {
  66. props = _props
  67. attrs = _attrs
  68. }
  69. },
  70. })
  71. const root = nodeOps.createElement('div')
  72. render(h(Comp, { foo: 1, bar: 2 }), root)
  73. expect(props).toEqual({ foo: 1 })
  74. expect(attrs).toEqual({ bar: 2 })
  75. render(h(Comp, { foo: 2, bar: 3, baz: 4 }), root)
  76. expect(props).toEqual({ foo: 2 })
  77. expect(attrs).toEqual({ bar: 3, baz: 4 })
  78. render(h(Comp, { qux: 5 }), root)
  79. expect(props).toEqual({})
  80. expect(attrs).toEqual({ qux: 5 })
  81. })
  82. test('functional with declaration', () => {
  83. let props: any
  84. let attrs: any
  85. const Comp: FunctionalComponent = (_props, { attrs: _attrs }) => {
  86. props = _props
  87. attrs = _attrs
  88. }
  89. Comp.props = ['foo']
  90. const root = nodeOps.createElement('div')
  91. render(h(Comp, { foo: 1, bar: 2 }), root)
  92. expect(props).toEqual({ foo: 1 })
  93. expect(attrs).toEqual({ bar: 2 })
  94. render(h(Comp, { foo: 2, bar: 3, baz: 4 }), root)
  95. expect(props).toEqual({ foo: 2 })
  96. expect(attrs).toEqual({ bar: 3, baz: 4 })
  97. render(h(Comp, { qux: 5 }), root)
  98. expect(props).toEqual({})
  99. expect(attrs).toEqual({ qux: 5 })
  100. })
  101. test('functional without declaration', () => {
  102. let props: any
  103. let attrs: any
  104. const Comp: FunctionalComponent = (_props, { attrs: _attrs }) => {
  105. props = _props
  106. attrs = _attrs
  107. }
  108. const root = nodeOps.createElement('div')
  109. render(h(Comp, { foo: 1 }), root)
  110. expect(props).toEqual({ foo: 1 })
  111. expect(attrs).toEqual({ foo: 1 })
  112. expect(props).toBe(attrs)
  113. render(h(Comp, { bar: 2 }), root)
  114. expect(props).toEqual({ bar: 2 })
  115. expect(attrs).toEqual({ bar: 2 })
  116. expect(props).toBe(attrs)
  117. })
  118. test('boolean casting', () => {
  119. let proxy: any
  120. const Comp = {
  121. props: {
  122. foo: Boolean,
  123. bar: Boolean,
  124. baz: Boolean,
  125. qux: Boolean,
  126. },
  127. render() {
  128. proxy = this
  129. },
  130. }
  131. render(
  132. h(Comp, {
  133. // absent should cast to false
  134. bar: '', // empty string should cast to true
  135. baz: 'baz', // same string should cast to true
  136. qux: 'ok', // other values should be left in-tact (but raise warning)
  137. }),
  138. nodeOps.createElement('div'),
  139. )
  140. expect(proxy.foo).toBe(false)
  141. expect(proxy.bar).toBe(true)
  142. expect(proxy.baz).toBe(true)
  143. expect(proxy.qux).toBe('ok')
  144. expect('type check failed for prop "qux"').toHaveBeenWarned()
  145. })
  146. test('default value', () => {
  147. let proxy: any
  148. const defaultFn = vi.fn(() => ({ a: 1 }))
  149. const defaultBaz = vi.fn(() => ({ b: 1 }))
  150. const Comp = {
  151. props: {
  152. foo: {
  153. default: 1,
  154. },
  155. bar: {
  156. default: defaultFn,
  157. },
  158. baz: {
  159. type: Function,
  160. default: defaultBaz,
  161. },
  162. },
  163. render() {
  164. proxy = this
  165. },
  166. }
  167. const root = nodeOps.createElement('div')
  168. render(h(Comp, { foo: 2 }), root)
  169. expect(proxy.foo).toBe(2)
  170. const prevBar = proxy.bar
  171. expect(proxy.bar).toEqual({ a: 1 })
  172. expect(proxy.baz).toEqual(defaultBaz)
  173. expect(defaultFn).toHaveBeenCalledTimes(1)
  174. expect(defaultBaz).toHaveBeenCalledTimes(0)
  175. // #999: updates should not cause default factory of unchanged prop to be
  176. // called again
  177. render(h(Comp, { foo: 3 }), root)
  178. expect(proxy.foo).toBe(3)
  179. expect(proxy.bar).toEqual({ a: 1 })
  180. expect(proxy.bar).toBe(prevBar)
  181. expect(defaultFn).toHaveBeenCalledTimes(1)
  182. render(h(Comp, { bar: { b: 2 } }), root)
  183. expect(proxy.foo).toBe(1)
  184. expect(proxy.bar).toEqual({ b: 2 })
  185. expect(defaultFn).toHaveBeenCalledTimes(1)
  186. render(h(Comp, { foo: 3, bar: { b: 3 } }), root)
  187. expect(proxy.foo).toBe(3)
  188. expect(proxy.bar).toEqual({ b: 3 })
  189. expect(defaultFn).toHaveBeenCalledTimes(1)
  190. render(h(Comp, { bar: { b: 4 } }), root)
  191. expect(proxy.foo).toBe(1)
  192. expect(proxy.bar).toEqual({ b: 4 })
  193. expect(defaultFn).toHaveBeenCalledTimes(1)
  194. })
  195. test('using inject in default value factory', () => {
  196. const Child = defineComponent({
  197. props: {
  198. test: {
  199. default: () => inject('test', 'default'),
  200. },
  201. },
  202. setup(props) {
  203. return () => {
  204. return h('div', props.test)
  205. }
  206. },
  207. })
  208. const Comp = {
  209. setup() {
  210. provide('test', 'injected')
  211. return () => h(Child)
  212. },
  213. }
  214. const root = nodeOps.createElement('div')
  215. render(h(Comp), root)
  216. expect(serializeInner(root)).toBe(`<div>injected</div>`)
  217. })
  218. test('optimized props updates', async () => {
  219. const Child = defineComponent({
  220. props: ['foo'],
  221. template: `<div>{{ foo }}</div>`,
  222. })
  223. const foo = ref(1)
  224. const id = ref('a')
  225. const Comp = defineComponent({
  226. setup() {
  227. return {
  228. foo,
  229. id,
  230. }
  231. },
  232. components: { Child },
  233. template: `<Child :foo="foo" :id="id"/>`,
  234. })
  235. // Note this one is using the main Vue render so it can compile template
  236. // on the fly
  237. const root = document.createElement('div')
  238. domRender(h(Comp), root)
  239. expect(root.innerHTML).toBe('<div id="a">1</div>')
  240. foo.value++
  241. await nextTick()
  242. expect(root.innerHTML).toBe('<div id="a">2</div>')
  243. id.value = 'b'
  244. await nextTick()
  245. expect(root.innerHTML).toBe('<div id="b">2</div>')
  246. })
  247. describe('validator', () => {
  248. test('validator should be called with two arguments', async () => {
  249. const mockFn = vi.fn((...args: any[]) => true)
  250. const Comp = defineComponent({
  251. props: {
  252. foo: {
  253. type: Number,
  254. validator: (value, props) => mockFn(value, props),
  255. },
  256. bar: {
  257. type: Number,
  258. },
  259. },
  260. template: `<div />`,
  261. })
  262. // Note this one is using the main Vue render so it can compile template
  263. // on the fly
  264. const root = document.createElement('div')
  265. domRender(h(Comp, { foo: 1, bar: 2 }), root)
  266. expect(mockFn).toHaveBeenCalledWith(1, { foo: 1, bar: 2 })
  267. })
  268. test('validator should not be able to mutate other props', async () => {
  269. const mockFn = vi.fn((...args: any[]) => true)
  270. const Comp = defineComponent({
  271. props: {
  272. foo: {
  273. type: Number,
  274. validator: (value, props) => !!(props.bar = 1),
  275. },
  276. bar: {
  277. type: Number,
  278. validator: value => mockFn(value),
  279. },
  280. },
  281. template: `<div />`,
  282. })
  283. // Note this one is using the main Vue render so it can compile template
  284. // on the fly
  285. const root = document.createElement('div')
  286. domRender(h(Comp, { foo: 1, bar: 2 }), root)
  287. expect(
  288. `Set operation on key "bar" failed: target is readonly.`,
  289. ).toHaveBeenWarnedLast()
  290. expect(mockFn).toHaveBeenCalledWith(2)
  291. })
  292. })
  293. test('warn props mutation', () => {
  294. let instance: ComponentInternalInstance
  295. let setupProps: any
  296. const Comp = {
  297. props: ['foo'],
  298. setup(props: any) {
  299. instance = getCurrentInstance()!
  300. setupProps = props
  301. return () => null
  302. },
  303. }
  304. render(h(Comp, { foo: 1 }), nodeOps.createElement('div'))
  305. expect(setupProps.foo).toBe(1)
  306. expect(instance!.props.foo).toBe(1)
  307. setupProps.foo = 2
  308. expect(`Set operation on key "foo" failed`).toHaveBeenWarned()
  309. expect(() => {
  310. ;(instance!.proxy as any).foo = 2
  311. }).toThrow(TypeError)
  312. expect(`Attempting to mutate prop "foo"`).toHaveBeenWarned()
  313. // should not throw when overriding properties other than props
  314. expect(() => {
  315. ;(instance!.proxy as any).hasOwnProperty = () => {}
  316. }).not.toThrow(TypeError)
  317. })
  318. test('warn absent required props', () => {
  319. const Comp = {
  320. props: {
  321. bool: { type: Boolean, required: true },
  322. str: { type: String, required: true },
  323. num: { type: Number, required: true },
  324. },
  325. setup() {
  326. return () => null
  327. },
  328. }
  329. render(h(Comp), nodeOps.createElement('div'))
  330. expect(`Missing required prop: "bool"`).toHaveBeenWarned()
  331. expect(`Missing required prop: "str"`).toHaveBeenWarned()
  332. expect(`Missing required prop: "num"`).toHaveBeenWarned()
  333. })
  334. test('warn on type mismatch', () => {
  335. class MyClass {}
  336. const Comp = {
  337. props: {
  338. bool: { type: Boolean },
  339. str: { type: String },
  340. num: { type: Number },
  341. arr: { type: Array },
  342. obj: { type: Object },
  343. cls: { type: MyClass },
  344. fn: { type: Function },
  345. skipCheck: { type: [Boolean, Function], skipCheck: true },
  346. empty: { type: [] },
  347. },
  348. setup() {
  349. return () => null
  350. },
  351. }
  352. render(
  353. h(Comp, {
  354. bool: 'true',
  355. str: 100,
  356. num: '100',
  357. arr: {},
  358. obj: 'false',
  359. cls: {},
  360. fn: true,
  361. skipCheck: 'foo',
  362. empty: [1, 2, 3],
  363. }),
  364. nodeOps.createElement('div'),
  365. )
  366. expect(
  367. `Invalid prop: type check failed for prop "bool". Expected Boolean, got String`,
  368. ).toHaveBeenWarned()
  369. expect(
  370. `Invalid prop: type check failed for prop "str". Expected String with value "100", got Number with value 100.`,
  371. ).toHaveBeenWarned()
  372. expect(
  373. `Invalid prop: type check failed for prop "num". Expected Number with value 100, got String with value "100".`,
  374. ).toHaveBeenWarned()
  375. expect(
  376. `Invalid prop: type check failed for prop "arr". Expected Array, got Object`,
  377. ).toHaveBeenWarned()
  378. expect(
  379. `Invalid prop: type check failed for prop "obj". Expected Object, got String with value "false"`,
  380. ).toHaveBeenWarned()
  381. expect(
  382. `Invalid prop: type check failed for prop "fn". Expected Function, got Boolean with value true.`,
  383. ).toHaveBeenWarned()
  384. expect(
  385. `Invalid prop: type check failed for prop "cls". Expected MyClass, got Object`,
  386. ).toHaveBeenWarned()
  387. expect(
  388. `Invalid prop: type check failed for prop "skipCheck". Expected Boolean | Function, got String with value "foo".`,
  389. ).not.toHaveBeenWarned()
  390. expect(
  391. `Prop type [] for prop "empty" won't match anything. Did you mean to use type Array instead?`,
  392. ).toHaveBeenWarned()
  393. })
  394. // #3495
  395. test('should not warn required props using kebab-case', async () => {
  396. const Comp = {
  397. props: {
  398. fooBar: { type: String, required: true },
  399. },
  400. setup() {
  401. return () => null
  402. },
  403. }
  404. render(
  405. h(Comp, {
  406. 'foo-bar': 'hello',
  407. }),
  408. nodeOps.createElement('div'),
  409. )
  410. expect(`Missing required prop: "fooBar"`).not.toHaveBeenWarned()
  411. })
  412. test('merging props from mixins and extends', () => {
  413. let setupProps: any
  414. let renderProxy: any
  415. const E = {
  416. props: ['base'],
  417. }
  418. const M1 = {
  419. props: ['m1'],
  420. }
  421. const M2 = {
  422. props: { m2: null },
  423. }
  424. const Comp = {
  425. props: ['self'],
  426. mixins: [M1, M2],
  427. extends: E,
  428. setup(props: any) {
  429. setupProps = props
  430. },
  431. render(this: any) {
  432. renderProxy = this
  433. return h('div', [this.self, this.base, this.m1, this.m2])
  434. },
  435. }
  436. const root = nodeOps.createElement('div')
  437. const props = {
  438. self: 'from self, ',
  439. base: 'from base, ',
  440. m1: 'from mixin 1, ',
  441. m2: 'from mixin 2',
  442. }
  443. render(h(Comp, props), root)
  444. expect(serializeInner(root)).toMatch(
  445. `from self, from base, from mixin 1, from mixin 2`,
  446. )
  447. expect(setupProps).toMatchObject(props)
  448. expect(renderProxy.$props).toMatchObject(props)
  449. })
  450. test('merging props from global mixins', () => {
  451. let setupProps: any
  452. let renderProxy: any
  453. const M1 = {
  454. props: ['m1'],
  455. }
  456. const M2 = {
  457. props: { m2: null },
  458. }
  459. const Comp = {
  460. props: ['self'],
  461. setup(props: any) {
  462. setupProps = props
  463. },
  464. render(this: any) {
  465. renderProxy = this
  466. return h('div', [this.self, this.m1, this.m2])
  467. },
  468. }
  469. const props = {
  470. self: 'from self, ',
  471. m1: 'from mixin 1, ',
  472. m2: 'from mixin 2',
  473. }
  474. const app = createApp(Comp, props)
  475. app.mixin(M1)
  476. app.mixin(M2)
  477. const root = nodeOps.createElement('div')
  478. app.mount(root)
  479. expect(serializeInner(root)).toMatch(
  480. `from self, from mixin 1, from mixin 2`,
  481. )
  482. expect(setupProps).toMatchObject(props)
  483. expect(renderProxy.$props).toMatchObject(props)
  484. })
  485. test('props type support BigInt', () => {
  486. const Comp = {
  487. props: {
  488. foo: BigInt,
  489. },
  490. render(this: any) {
  491. return h('div', [this.foo])
  492. },
  493. }
  494. const root = nodeOps.createElement('div')
  495. render(
  496. h(Comp, {
  497. foo: BigInt(BigInt(100000111)) + BigInt(2000000000) * BigInt(30000000),
  498. }),
  499. root,
  500. )
  501. expect(serializeInner(root)).toMatch('<div>60000000100000111</div>')
  502. })
  503. // #3474
  504. test('should cache the value returned from the default factory to avoid unnecessary watcher trigger', async () => {
  505. let count = 0
  506. const Comp = {
  507. props: {
  508. foo: {
  509. type: Object,
  510. default: () => ({ val: 1 }),
  511. },
  512. bar: Number,
  513. },
  514. setup(props: any) {
  515. watch(
  516. () => props.foo,
  517. () => {
  518. count++
  519. },
  520. )
  521. return () => h('h1', [props.foo.val, props.bar])
  522. },
  523. }
  524. const foo = ref()
  525. const bar = ref(0)
  526. const app = createApp({
  527. render: () => h(Comp, { foo: foo.value, bar: bar.value }),
  528. })
  529. const root = nodeOps.createElement('div')
  530. app.mount(root)
  531. expect(serializeInner(root)).toMatch(`<h1>10</h1>`)
  532. expect(count).toBe(0)
  533. bar.value++
  534. await nextTick()
  535. expect(serializeInner(root)).toMatch(`<h1>11</h1>`)
  536. expect(count).toBe(0)
  537. })
  538. // #3288
  539. test('declared prop key should be present even if not passed', async () => {
  540. let initialKeys: string[] = []
  541. const changeSpy = vi.fn()
  542. const passFoo = ref(false)
  543. const Comp = {
  544. render() {},
  545. props: {
  546. foo: String,
  547. },
  548. setup(props: any) {
  549. initialKeys = Object.keys(props)
  550. const { foo } = toRefs(props)
  551. watch(foo, changeSpy)
  552. },
  553. }
  554. const Parent = () => (passFoo.value ? h(Comp, { foo: 'ok' }) : h(Comp))
  555. const root = nodeOps.createElement('div')
  556. createApp(Parent).mount(root)
  557. expect(initialKeys).toMatchObject(['foo'])
  558. passFoo.value = true
  559. await nextTick()
  560. expect(changeSpy).toHaveBeenCalledTimes(1)
  561. })
  562. // #3371
  563. test(`avoid double-setting props when casting`, async () => {
  564. const Parent = {
  565. setup(props: any, { slots }: SetupContext) {
  566. const childProps = ref()
  567. const registerChildProps = (props: any) => {
  568. childProps.value = props
  569. }
  570. provide('register', registerChildProps)
  571. return () => {
  572. // access the child component's props
  573. childProps.value && childProps.value.foo
  574. return slots.default!()
  575. }
  576. },
  577. }
  578. const Child = {
  579. props: {
  580. foo: {
  581. type: Boolean,
  582. required: false,
  583. },
  584. },
  585. setup(props: { foo: boolean }) {
  586. const register = inject('register') as any
  587. // 1. change the reactivity data of the parent component
  588. // 2. register its own props to the parent component
  589. register(props)
  590. return () => 'foo'
  591. },
  592. }
  593. const App = {
  594. setup() {
  595. return () => h(Parent, () => h(Child as any, { foo: '' }, () => null))
  596. },
  597. }
  598. const root = nodeOps.createElement('div')
  599. render(h(App), root)
  600. await nextTick()
  601. expect(serializeInner(root)).toBe(`foo`)
  602. })
  603. test('support null in required + multiple-type declarations', () => {
  604. const Comp = {
  605. props: {
  606. foo: { type: [Function, null], required: true },
  607. },
  608. render() {},
  609. }
  610. const root = nodeOps.createElement('div')
  611. expect(() => {
  612. render(h(Comp, { foo: () => {} }), root)
  613. }).not.toThrow()
  614. expect(() => {
  615. render(h(Comp, { foo: null }), root)
  616. }).not.toThrow()
  617. })
  618. // #5016
  619. test('handling attr with undefined value', () => {
  620. const Comp = {
  621. render(this: any) {
  622. return JSON.stringify(this.$attrs) + Object.keys(this.$attrs)
  623. },
  624. }
  625. const root = nodeOps.createElement('div')
  626. let attrs: any = { foo: undefined }
  627. render(h(Comp, attrs), root)
  628. expect(serializeInner(root)).toBe(
  629. JSON.stringify(attrs) + Object.keys(attrs),
  630. )
  631. render(h(Comp, (attrs = { foo: 'bar' })), root)
  632. expect(serializeInner(root)).toBe(
  633. JSON.stringify(attrs) + Object.keys(attrs),
  634. )
  635. })
  636. // #691ef
  637. test('should not mutate original props long-form definition object', () => {
  638. const props = {
  639. msg: {
  640. type: String,
  641. },
  642. }
  643. const Comp = defineComponent({
  644. props,
  645. render() {},
  646. })
  647. const root = nodeOps.createElement('div')
  648. render(h(Comp, { msg: 'test' }), root)
  649. expect(Object.keys(props.msg).length).toBe(1)
  650. })
  651. test('should warn against reserved prop names', () => {
  652. const Comp = defineComponent({
  653. props: {
  654. key: String,
  655. ref: String,
  656. $foo: String,
  657. },
  658. render() {},
  659. })
  660. const root = nodeOps.createElement('div')
  661. render(h(Comp, { msg: 'test' }), root)
  662. expect(`Invalid prop name: "key"`).toHaveBeenWarned()
  663. expect(`Invalid prop name: "ref"`).toHaveBeenWarned()
  664. expect(`Invalid prop name: "$foo"`).toHaveBeenWarned()
  665. })
  666. // #5517
  667. test('events should not be props when component updating', async () => {
  668. let props: any
  669. function eventHandler() {}
  670. const foo = ref(1)
  671. const Child = defineComponent({
  672. setup(_props) {
  673. props = _props
  674. },
  675. emits: ['event'],
  676. props: ['foo'],
  677. template: `<div>{{ foo }}</div>`,
  678. })
  679. const Comp = defineComponent({
  680. setup() {
  681. return {
  682. foo,
  683. eventHandler,
  684. }
  685. },
  686. components: { Child },
  687. template: `<Child @event="eventHandler" :foo="foo" />`,
  688. })
  689. const root = document.createElement('div')
  690. domRender(h(Comp), root)
  691. expect(props).not.toHaveProperty('onEvent')
  692. foo.value++
  693. await nextTick()
  694. expect(props).not.toHaveProperty('onEvent')
  695. })
  696. })