apiOptions.spec.ts 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159
  1. import {
  2. h,
  3. nodeOps,
  4. render,
  5. serializeInner,
  6. triggerEvent,
  7. TestElement,
  8. nextTick,
  9. renderToString,
  10. ref,
  11. defineComponent
  12. } from '@vue/runtime-test'
  13. describe('api: options', () => {
  14. test('data', async () => {
  15. const Comp = defineComponent({
  16. data() {
  17. return {
  18. foo: 1
  19. }
  20. },
  21. render() {
  22. return h(
  23. 'div',
  24. {
  25. onClick: () => {
  26. this.foo++
  27. }
  28. },
  29. this.foo
  30. )
  31. }
  32. })
  33. const root = nodeOps.createElement('div')
  34. render(h(Comp), root)
  35. expect(serializeInner(root)).toBe(`<div>1</div>`)
  36. triggerEvent(root.children[0] as TestElement, 'click')
  37. await nextTick()
  38. expect(serializeInner(root)).toBe(`<div>2</div>`)
  39. })
  40. test('computed', async () => {
  41. const Comp = defineComponent({
  42. data() {
  43. return {
  44. foo: 1
  45. }
  46. },
  47. computed: {
  48. bar(): number {
  49. return this.foo + 1
  50. },
  51. baz: (vm): number => vm.bar + 1
  52. },
  53. render() {
  54. return h(
  55. 'div',
  56. {
  57. onClick: () => {
  58. this.foo++
  59. }
  60. },
  61. this.bar + this.baz
  62. )
  63. }
  64. })
  65. const root = nodeOps.createElement('div')
  66. render(h(Comp), root)
  67. expect(serializeInner(root)).toBe(`<div>5</div>`)
  68. triggerEvent(root.children[0] as TestElement, 'click')
  69. await nextTick()
  70. expect(serializeInner(root)).toBe(`<div>7</div>`)
  71. })
  72. test('methods', async () => {
  73. const Comp = defineComponent({
  74. data() {
  75. return {
  76. foo: 1
  77. }
  78. },
  79. methods: {
  80. inc() {
  81. this.foo++
  82. }
  83. },
  84. render() {
  85. return h(
  86. 'div',
  87. {
  88. onClick: this.inc
  89. },
  90. this.foo
  91. )
  92. }
  93. })
  94. const root = nodeOps.createElement('div')
  95. render(h(Comp), root)
  96. expect(serializeInner(root)).toBe(`<div>1</div>`)
  97. triggerEvent(root.children[0] as TestElement, 'click')
  98. await nextTick()
  99. expect(serializeInner(root)).toBe(`<div>2</div>`)
  100. })
  101. test('watch', async () => {
  102. function returnThis(this: any) {
  103. return this
  104. }
  105. const spyA = jest.fn(returnThis)
  106. const spyB = jest.fn(returnThis)
  107. const spyC = jest.fn(returnThis)
  108. const spyD = jest.fn(returnThis)
  109. let ctx: any
  110. const Comp = {
  111. data() {
  112. return {
  113. foo: 1,
  114. bar: 2,
  115. baz: {
  116. qux: 3
  117. },
  118. qux: 4
  119. }
  120. },
  121. watch: {
  122. // string method name
  123. foo: 'onFooChange',
  124. // direct function
  125. bar: spyB,
  126. baz: {
  127. handler: spyC,
  128. deep: true
  129. },
  130. qux: {
  131. handler: 'onQuxChange'
  132. }
  133. },
  134. methods: {
  135. onFooChange: spyA,
  136. onQuxChange: spyD
  137. },
  138. render() {
  139. ctx = this
  140. }
  141. }
  142. const root = nodeOps.createElement('div')
  143. render(h(Comp), root)
  144. function assertCall(spy: jest.Mock, callIndex: number, args: any[]) {
  145. expect(spy.mock.calls[callIndex].slice(0, 2)).toMatchObject(args)
  146. expect(spy).toHaveReturnedWith(ctx)
  147. }
  148. ctx.foo++
  149. await nextTick()
  150. expect(spyA).toHaveBeenCalledTimes(1)
  151. assertCall(spyA, 0, [2, 1])
  152. ctx.bar++
  153. await nextTick()
  154. expect(spyB).toHaveBeenCalledTimes(1)
  155. assertCall(spyB, 0, [3, 2])
  156. ctx.baz.qux++
  157. await nextTick()
  158. expect(spyC).toHaveBeenCalledTimes(1)
  159. // new and old objects have same identity
  160. assertCall(spyC, 0, [{ qux: 4 }, { qux: 4 }])
  161. ctx.qux++
  162. await nextTick()
  163. expect(spyD).toHaveBeenCalledTimes(1)
  164. assertCall(spyD, 0, [5, 4])
  165. })
  166. test('watch array', async () => {
  167. function returnThis(this: any) {
  168. return this
  169. }
  170. const spyA = jest.fn(returnThis)
  171. const spyB = jest.fn(returnThis)
  172. const spyC = jest.fn(returnThis)
  173. let ctx: any
  174. const Comp = {
  175. data() {
  176. return {
  177. foo: 1,
  178. bar: 2,
  179. baz: {
  180. qux: 3
  181. }
  182. }
  183. },
  184. watch: {
  185. // string method name
  186. foo: ['onFooChange'],
  187. // direct function
  188. bar: [spyB],
  189. baz: [
  190. {
  191. handler: spyC,
  192. deep: true
  193. }
  194. ]
  195. },
  196. methods: {
  197. onFooChange: spyA
  198. },
  199. render() {
  200. ctx = this
  201. }
  202. }
  203. const root = nodeOps.createElement('div')
  204. render(h(Comp), root)
  205. function assertCall(spy: jest.Mock, callIndex: number, args: any[]) {
  206. expect(spy.mock.calls[callIndex].slice(0, 2)).toMatchObject(args)
  207. expect(spy).toHaveReturnedWith(ctx)
  208. }
  209. ctx.foo++
  210. await nextTick()
  211. expect(spyA).toHaveBeenCalledTimes(1)
  212. assertCall(spyA, 0, [2, 1])
  213. ctx.bar++
  214. await nextTick()
  215. expect(spyB).toHaveBeenCalledTimes(1)
  216. assertCall(spyB, 0, [3, 2])
  217. ctx.baz.qux++
  218. await nextTick()
  219. expect(spyC).toHaveBeenCalledTimes(1)
  220. // new and old objects have same identity
  221. assertCall(spyC, 0, [{ qux: 4 }, { qux: 4 }])
  222. })
  223. test('provide/inject', () => {
  224. const Root = {
  225. data() {
  226. return {
  227. a: 1
  228. }
  229. },
  230. provide() {
  231. return {
  232. a: this.a
  233. }
  234. },
  235. render() {
  236. return [h(ChildA), h(ChildB), h(ChildC), h(ChildD)]
  237. }
  238. } as any
  239. const ChildA = {
  240. inject: ['a'],
  241. render() {
  242. return this.a
  243. }
  244. } as any
  245. const ChildB = {
  246. // object alias
  247. inject: { b: 'a' },
  248. render() {
  249. return this.b
  250. }
  251. } as any
  252. const ChildC = {
  253. inject: {
  254. b: {
  255. from: 'a'
  256. }
  257. },
  258. render() {
  259. return this.b
  260. }
  261. } as any
  262. const ChildD = {
  263. inject: {
  264. b: {
  265. from: 'c',
  266. default: 2
  267. }
  268. },
  269. render() {
  270. return this.b
  271. }
  272. } as any
  273. expect(renderToString(h(Root))).toBe(`1112`)
  274. })
  275. test('lifecycle', async () => {
  276. const count = ref(0)
  277. const root = nodeOps.createElement('div')
  278. const calls: string[] = []
  279. const Root = {
  280. beforeCreate() {
  281. calls.push('root beforeCreate')
  282. },
  283. created() {
  284. calls.push('root created')
  285. },
  286. beforeMount() {
  287. calls.push('root onBeforeMount')
  288. },
  289. mounted() {
  290. calls.push('root onMounted')
  291. },
  292. beforeUpdate() {
  293. calls.push('root onBeforeUpdate')
  294. },
  295. updated() {
  296. calls.push('root onUpdated')
  297. },
  298. beforeUnmount() {
  299. calls.push('root onBeforeUnmount')
  300. },
  301. unmounted() {
  302. calls.push('root onUnmounted')
  303. },
  304. render() {
  305. return h(Mid, { count: count.value })
  306. }
  307. }
  308. const Mid = {
  309. beforeCreate() {
  310. calls.push('mid beforeCreate')
  311. },
  312. created() {
  313. calls.push('mid created')
  314. },
  315. beforeMount() {
  316. calls.push('mid onBeforeMount')
  317. },
  318. mounted() {
  319. calls.push('mid onMounted')
  320. },
  321. beforeUpdate() {
  322. calls.push('mid onBeforeUpdate')
  323. },
  324. updated() {
  325. calls.push('mid onUpdated')
  326. },
  327. beforeUnmount() {
  328. calls.push('mid onBeforeUnmount')
  329. },
  330. unmounted() {
  331. calls.push('mid onUnmounted')
  332. },
  333. render(this: any) {
  334. return h(Child, { count: this.$props.count })
  335. }
  336. }
  337. const Child = {
  338. beforeCreate() {
  339. calls.push('child beforeCreate')
  340. },
  341. created() {
  342. calls.push('child created')
  343. },
  344. beforeMount() {
  345. calls.push('child onBeforeMount')
  346. },
  347. mounted() {
  348. calls.push('child onMounted')
  349. },
  350. beforeUpdate() {
  351. calls.push('child onBeforeUpdate')
  352. },
  353. updated() {
  354. calls.push('child onUpdated')
  355. },
  356. beforeUnmount() {
  357. calls.push('child onBeforeUnmount')
  358. },
  359. unmounted() {
  360. calls.push('child onUnmounted')
  361. },
  362. render(this: any) {
  363. return h('div', this.$props.count)
  364. }
  365. }
  366. // mount
  367. render(h(Root), root)
  368. expect(calls).toEqual([
  369. 'root beforeCreate',
  370. 'root created',
  371. 'root onBeforeMount',
  372. 'mid beforeCreate',
  373. 'mid created',
  374. 'mid onBeforeMount',
  375. 'child beforeCreate',
  376. 'child created',
  377. 'child onBeforeMount',
  378. 'child onMounted',
  379. 'mid onMounted',
  380. 'root onMounted'
  381. ])
  382. calls.length = 0
  383. // update
  384. count.value++
  385. await nextTick()
  386. expect(calls).toEqual([
  387. 'root onBeforeUpdate',
  388. 'mid onBeforeUpdate',
  389. 'child onBeforeUpdate',
  390. 'child onUpdated',
  391. 'mid onUpdated',
  392. 'root onUpdated'
  393. ])
  394. calls.length = 0
  395. // unmount
  396. render(null, root)
  397. expect(calls).toEqual([
  398. 'root onBeforeUnmount',
  399. 'mid onBeforeUnmount',
  400. 'child onBeforeUnmount',
  401. 'child onUnmounted',
  402. 'mid onUnmounted',
  403. 'root onUnmounted'
  404. ])
  405. })
  406. test('mixins', () => {
  407. const calls: string[] = []
  408. const mixinA = {
  409. data() {
  410. return {
  411. a: 1
  412. }
  413. },
  414. created(this: any) {
  415. calls.push('mixinA created')
  416. expect(this.a).toBe(1)
  417. expect(this.b).toBe(2)
  418. expect(this.c).toBe(4)
  419. },
  420. mounted() {
  421. calls.push('mixinA mounted')
  422. }
  423. }
  424. const mixinB = {
  425. props: {
  426. bP: {
  427. type: String
  428. }
  429. },
  430. data() {
  431. return {
  432. b: 2
  433. }
  434. },
  435. created(this: any) {
  436. calls.push('mixinB created')
  437. expect(this.a).toBe(1)
  438. expect(this.b).toBe(2)
  439. expect(this.bP).toBeUndefined()
  440. expect(this.c).toBe(4)
  441. expect(this.cP1).toBeUndefined()
  442. },
  443. mounted() {
  444. calls.push('mixinB mounted')
  445. }
  446. }
  447. const mixinC = defineComponent({
  448. props: ['cP1', 'cP2'],
  449. data() {
  450. return {
  451. c: 3
  452. }
  453. },
  454. created() {
  455. calls.push('mixinC created')
  456. // component data() should overwrite mixin field with same key
  457. expect(this.c).toBe(4)
  458. expect(this.cP1).toBeUndefined()
  459. },
  460. mounted() {
  461. calls.push('mixinC mounted')
  462. }
  463. })
  464. const Comp = defineComponent({
  465. props: {
  466. aaa: String
  467. },
  468. mixins: [defineComponent(mixinA), defineComponent(mixinB), mixinC],
  469. data() {
  470. return {
  471. c: 4,
  472. z: 4
  473. }
  474. },
  475. created() {
  476. calls.push('comp created')
  477. expect(this.a).toBe(1)
  478. expect(this.b).toBe(2)
  479. expect(this.bP).toBeUndefined()
  480. expect(this.c).toBe(4)
  481. expect(this.cP2).toBeUndefined()
  482. expect(this.z).toBe(4)
  483. },
  484. mounted() {
  485. calls.push('comp mounted')
  486. },
  487. render() {
  488. return `${this.a}${this.b}${this.c}`
  489. }
  490. })
  491. expect(renderToString(h(Comp))).toBe(`124`)
  492. expect(calls).toEqual([
  493. 'mixinA created',
  494. 'mixinB created',
  495. 'mixinC created',
  496. 'comp created',
  497. 'mixinA mounted',
  498. 'mixinB mounted',
  499. 'mixinC mounted',
  500. 'comp mounted'
  501. ])
  502. })
  503. test('render from mixin', () => {
  504. const Comp = {
  505. mixins: [
  506. {
  507. render: () => 'from mixin'
  508. }
  509. ]
  510. }
  511. expect(renderToString(h(Comp))).toBe('from mixin')
  512. })
  513. test('extends', () => {
  514. const calls: string[] = []
  515. const Base = {
  516. data() {
  517. return {
  518. a: 1,
  519. b: 1
  520. }
  521. },
  522. methods: {
  523. sayA() {}
  524. },
  525. mounted(this: any) {
  526. expect(this.a).toBe(1)
  527. expect(this.b).toBe(2)
  528. calls.push('base')
  529. }
  530. }
  531. const Comp = defineComponent({
  532. extends: defineComponent(Base),
  533. data() {
  534. return {
  535. b: 2
  536. }
  537. },
  538. mounted() {
  539. calls.push('comp')
  540. },
  541. render() {
  542. return `${this.a}${this.b}`
  543. }
  544. })
  545. expect(renderToString(h(Comp))).toBe(`12`)
  546. expect(calls).toEqual(['base', 'comp'])
  547. })
  548. test('extends with mixins', () => {
  549. const calls: string[] = []
  550. const Base = {
  551. data() {
  552. return {
  553. a: 1,
  554. x: 'base'
  555. }
  556. },
  557. methods: {
  558. sayA() {}
  559. },
  560. mounted(this: any) {
  561. expect(this.a).toBe(1)
  562. expect(this.b).toBeTruthy()
  563. expect(this.c).toBe(2)
  564. calls.push('base')
  565. }
  566. }
  567. const Mixin = {
  568. data() {
  569. return {
  570. b: true,
  571. x: 'mixin'
  572. }
  573. },
  574. mounted(this: any) {
  575. expect(this.a).toBe(1)
  576. expect(this.b).toBeTruthy()
  577. expect(this.c).toBe(2)
  578. calls.push('mixin')
  579. }
  580. }
  581. const Comp = defineComponent({
  582. extends: defineComponent(Base),
  583. mixins: [defineComponent(Mixin)],
  584. data() {
  585. return {
  586. c: 2
  587. }
  588. },
  589. mounted() {
  590. calls.push('comp')
  591. },
  592. render() {
  593. return `${this.a}${this.b}${this.c}${this.x}`
  594. }
  595. })
  596. expect(renderToString(h(Comp))).toBe(`1true2mixin`)
  597. expect(calls).toEqual(['base', 'mixin', 'comp'])
  598. })
  599. test('beforeCreate/created in extends and mixins', () => {
  600. const calls: string[] = []
  601. const BaseA = {
  602. beforeCreate() {
  603. calls.push('beforeCreateA')
  604. },
  605. created() {
  606. calls.push('createdA')
  607. }
  608. }
  609. const BaseB = {
  610. extends: BaseA,
  611. beforeCreate() {
  612. calls.push('beforeCreateB')
  613. },
  614. created() {
  615. calls.push('createdB')
  616. }
  617. }
  618. const MixinA = {
  619. beforeCreate() {
  620. calls.push('beforeCreateC')
  621. },
  622. created() {
  623. calls.push('createdC')
  624. }
  625. }
  626. const MixinB = {
  627. mixins: [MixinA],
  628. beforeCreate() {
  629. calls.push('beforeCreateD')
  630. },
  631. created() {
  632. calls.push('createdD')
  633. }
  634. }
  635. const Comp = {
  636. extends: BaseB,
  637. mixins: [MixinB],
  638. beforeCreate() {
  639. calls.push('selfBeforeCreate')
  640. },
  641. created() {
  642. calls.push('selfCreated')
  643. },
  644. render() {}
  645. }
  646. renderToString(h(Comp))
  647. expect(calls).toEqual([
  648. 'beforeCreateA',
  649. 'beforeCreateB',
  650. 'beforeCreateC',
  651. 'beforeCreateD',
  652. 'selfBeforeCreate',
  653. 'createdA',
  654. 'createdB',
  655. 'createdC',
  656. 'createdD',
  657. 'selfCreated'
  658. ])
  659. })
  660. test('flatten merged options', async () => {
  661. const MixinBase = {
  662. msg1: 'base'
  663. }
  664. const ExtendsBase = {
  665. msg2: 'base'
  666. }
  667. const Mixin = {
  668. mixins: [MixinBase]
  669. }
  670. const Extends = {
  671. extends: ExtendsBase
  672. }
  673. const Comp = defineComponent({
  674. extends: defineComponent(Extends),
  675. mixins: [defineComponent(Mixin)],
  676. render() {
  677. return `${this.$options.msg1},${this.$options.msg2}`
  678. }
  679. })
  680. expect(renderToString(h(Comp))).toBe('base,base')
  681. })
  682. test('options defined in component have higher priority', async () => {
  683. const Mixin = {
  684. msg1: 'base'
  685. }
  686. const Extends = {
  687. msg2: 'base'
  688. }
  689. const Comp = defineComponent({
  690. msg1: 'local',
  691. msg2: 'local',
  692. extends: defineComponent(Extends),
  693. mixins: [defineComponent(Mixin)],
  694. render() {
  695. return `${this.$options.msg1},${this.$options.msg2}`
  696. }
  697. })
  698. expect(renderToString(h(Comp))).toBe('local,local')
  699. })
  700. test('accessing setup() state from options', async () => {
  701. const Comp = defineComponent({
  702. setup() {
  703. return {
  704. count: ref(0)
  705. }
  706. },
  707. data() {
  708. return {
  709. plusOne: (this as any).count + 1
  710. }
  711. },
  712. computed: {
  713. plusTwo(): number {
  714. return this.count + 2
  715. }
  716. },
  717. methods: {
  718. inc() {
  719. this.count++
  720. }
  721. },
  722. render() {
  723. return h(
  724. 'div',
  725. {
  726. onClick: this.inc
  727. },
  728. `${this.count},${this.plusOne},${this.plusTwo}`
  729. )
  730. }
  731. })
  732. const root = nodeOps.createElement('div')
  733. render(h(Comp), root)
  734. expect(serializeInner(root)).toBe(`<div>0,1,2</div>`)
  735. triggerEvent(root.children[0] as TestElement, 'click')
  736. await nextTick()
  737. expect(serializeInner(root)).toBe(`<div>1,1,3</div>`)
  738. })
  739. // #1016
  740. test('watcher initialization should be deferred in mixins', async () => {
  741. const mixin1 = {
  742. data() {
  743. return {
  744. mixin1Data: 'mixin1'
  745. }
  746. },
  747. methods: {}
  748. }
  749. const watchSpy = jest.fn()
  750. const mixin2 = {
  751. watch: {
  752. mixin3Data: watchSpy
  753. }
  754. }
  755. const mixin3 = {
  756. data() {
  757. return {
  758. mixin3Data: 'mixin3'
  759. }
  760. },
  761. methods: {}
  762. }
  763. let vm: any
  764. const Comp = {
  765. mixins: [mixin1, mixin2, mixin3],
  766. render() {},
  767. created() {
  768. vm = this
  769. }
  770. }
  771. const root = nodeOps.createElement('div')
  772. render(h(Comp), root)
  773. // should have no warnings
  774. vm.mixin3Data = 'hello'
  775. await nextTick()
  776. expect(watchSpy.mock.calls[0].slice(0, 2)).toEqual(['hello', 'mixin3'])
  777. })
  778. describe('warnings', () => {
  779. test('Expected a function as watch handler', () => {
  780. const Comp = {
  781. watch: {
  782. foo: 'notExistingMethod',
  783. foo2: {
  784. handler: 'notExistingMethod2'
  785. }
  786. },
  787. render() {}
  788. }
  789. const root = nodeOps.createElement('div')
  790. render(h(Comp), root)
  791. expect(
  792. 'Invalid watch handler specified by key "notExistingMethod"'
  793. ).toHaveBeenWarned()
  794. expect(
  795. 'Invalid watch handler specified by key "notExistingMethod2"'
  796. ).toHaveBeenWarned()
  797. })
  798. test('Invalid watch option', () => {
  799. const Comp = {
  800. watch: { foo: true },
  801. render() {}
  802. }
  803. const root = nodeOps.createElement('div')
  804. // @ts-ignore
  805. render(h(Comp), root)
  806. expect('Invalid watch option: "foo"').toHaveBeenWarned()
  807. })
  808. test('computed with setter and no getter', () => {
  809. const Comp = {
  810. computed: {
  811. foo: {
  812. set() {}
  813. }
  814. },
  815. render() {}
  816. }
  817. const root = nodeOps.createElement('div')
  818. render(h(Comp), root)
  819. expect('Computed property "foo" has no getter.').toHaveBeenWarned()
  820. })
  821. test('assigning to computed with no setter', () => {
  822. let instance: any
  823. const Comp = {
  824. computed: {
  825. foo: {
  826. get() {}
  827. }
  828. },
  829. mounted() {
  830. instance = this
  831. },
  832. render() {}
  833. }
  834. const root = nodeOps.createElement('div')
  835. render(h(Comp), root)
  836. instance.foo = 1
  837. expect(
  838. 'Write operation failed: computed property "foo" is readonly'
  839. ).toHaveBeenWarned()
  840. })
  841. test('inject property is already declared in props', () => {
  842. const Comp = {
  843. data() {
  844. return {
  845. a: 1
  846. }
  847. },
  848. provide() {
  849. return {
  850. a: this.a
  851. }
  852. },
  853. render() {
  854. return [h(ChildA)]
  855. }
  856. } as any
  857. const ChildA = {
  858. props: { a: Number },
  859. inject: ['a'],
  860. render() {
  861. return this.a
  862. }
  863. } as any
  864. const root = nodeOps.createElement('div')
  865. render(h(Comp), root)
  866. expect(
  867. `Inject property "a" is already defined in Props.`
  868. ).toHaveBeenWarned()
  869. })
  870. test('methods property is not a function', () => {
  871. const Comp = {
  872. methods: {
  873. foo: 1
  874. },
  875. render() {}
  876. }
  877. const root = nodeOps.createElement('div')
  878. render(h(Comp), root)
  879. expect(
  880. `Method "foo" has type "number" in the component definition. ` +
  881. `Did you reference the function correctly?`
  882. ).toHaveBeenWarned()
  883. })
  884. test('methods property is already declared in props', () => {
  885. const Comp = {
  886. props: {
  887. foo: Number
  888. },
  889. methods: {
  890. foo() {}
  891. },
  892. render() {}
  893. }
  894. const root = nodeOps.createElement('div')
  895. render(h(Comp), root)
  896. expect(
  897. `Methods property "foo" is already defined in Props.`
  898. ).toHaveBeenWarned()
  899. })
  900. test('methods property is already declared in inject', () => {
  901. const Comp = {
  902. data() {
  903. return {
  904. a: 1
  905. }
  906. },
  907. provide() {
  908. return {
  909. a: this.a
  910. }
  911. },
  912. render() {
  913. return [h(ChildA)]
  914. }
  915. } as any
  916. const ChildA = {
  917. methods: {
  918. a: () => null
  919. },
  920. inject: ['a'],
  921. render() {
  922. return this.a
  923. }
  924. } as any
  925. const root = nodeOps.createElement('div')
  926. render(h(Comp), root)
  927. expect(
  928. `Methods property "a" is already defined in Inject.`
  929. ).toHaveBeenWarned()
  930. })
  931. test('data property is already declared in props', () => {
  932. const Comp = {
  933. props: { foo: Number },
  934. data: () => ({
  935. foo: 1
  936. }),
  937. render() {}
  938. }
  939. const root = nodeOps.createElement('div')
  940. render(h(Comp), root)
  941. expect(
  942. `Data property "foo" is already defined in Props.`
  943. ).toHaveBeenWarned()
  944. })
  945. test('data property is already declared in inject', () => {
  946. const Comp = {
  947. data() {
  948. return {
  949. a: 1
  950. }
  951. },
  952. provide() {
  953. return {
  954. a: this.a
  955. }
  956. },
  957. render() {
  958. return [h(ChildA)]
  959. }
  960. } as any
  961. const ChildA = {
  962. data() {
  963. return {
  964. a: 1
  965. }
  966. },
  967. inject: ['a'],
  968. render() {
  969. return this.a
  970. }
  971. } as any
  972. const root = nodeOps.createElement('div')
  973. render(h(Comp), root)
  974. expect(
  975. `Data property "a" is already defined in Inject.`
  976. ).toHaveBeenWarned()
  977. })
  978. test('data property is already declared in methods', () => {
  979. const Comp = {
  980. data: () => ({
  981. foo: 1
  982. }),
  983. methods: {
  984. foo() {}
  985. },
  986. render() {}
  987. }
  988. const root = nodeOps.createElement('div')
  989. render(h(Comp), root)
  990. expect(
  991. `Data property "foo" is already defined in Methods.`
  992. ).toHaveBeenWarned()
  993. })
  994. test('computed property is already declared in props', () => {
  995. const Comp = {
  996. props: { foo: Number },
  997. computed: {
  998. foo() {}
  999. },
  1000. render() {}
  1001. }
  1002. const root = nodeOps.createElement('div')
  1003. render(h(Comp), root)
  1004. expect(
  1005. `Computed property "foo" is already defined in Props.`
  1006. ).toHaveBeenWarned()
  1007. })
  1008. test('computed property is already declared in inject', () => {
  1009. const Comp = {
  1010. data() {
  1011. return {
  1012. a: 1
  1013. }
  1014. },
  1015. provide() {
  1016. return {
  1017. a: this.a
  1018. }
  1019. },
  1020. render() {
  1021. return [h(ChildA)]
  1022. }
  1023. } as any
  1024. const ChildA = {
  1025. computed: {
  1026. a: {
  1027. get() {},
  1028. set() {}
  1029. }
  1030. },
  1031. inject: ['a'],
  1032. render() {
  1033. return this.a
  1034. }
  1035. } as any
  1036. const root = nodeOps.createElement('div')
  1037. render(h(Comp), root)
  1038. expect(
  1039. `Computed property "a" is already defined in Inject.`
  1040. ).toHaveBeenWarned()
  1041. })
  1042. test('computed property is already declared in methods', () => {
  1043. const Comp = {
  1044. computed: {
  1045. foo() {}
  1046. },
  1047. methods: {
  1048. foo() {}
  1049. },
  1050. render() {}
  1051. }
  1052. const root = nodeOps.createElement('div')
  1053. render(h(Comp), root)
  1054. expect(
  1055. `Computed property "foo" is already defined in Methods.`
  1056. ).toHaveBeenWarned()
  1057. })
  1058. test('computed property is already declared in data', () => {
  1059. const Comp = {
  1060. data: () => ({
  1061. foo: 1
  1062. }),
  1063. computed: {
  1064. foo() {}
  1065. },
  1066. render() {}
  1067. }
  1068. const root = nodeOps.createElement('div')
  1069. render(h(Comp), root)
  1070. expect(
  1071. `Computed property "foo" is already defined in Data.`
  1072. ).toHaveBeenWarned()
  1073. })
  1074. })
  1075. })