ssr-reactivity.spec.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // @vitest-environment node
  2. import Vue from 'vue'
  3. import {
  4. reactive,
  5. ref,
  6. isReactive,
  7. shallowRef,
  8. isRef,
  9. set,
  10. nextTick,
  11. getCurrentInstance
  12. } from 'v3'
  13. import { createRenderer } from '../src'
  14. describe('SSR Reactive', () => {
  15. beforeEach(() => {
  16. // force SSR env
  17. global.process.env.VUE_ENV = 'server'
  18. })
  19. it('should not affect non reactive APIs', () => {
  20. expect(typeof window).toBe('undefined')
  21. expect((Vue.observable({}) as any).__ob__).toBeUndefined()
  22. })
  23. it('reactive behavior should be consistent in SSR', () => {
  24. const obj = reactive({
  25. foo: ref(1),
  26. bar: {
  27. baz: ref(2)
  28. },
  29. arr: [{ foo: ref(3) }]
  30. })
  31. expect(isReactive(obj)).toBe(true)
  32. expect(obj.foo).toBe(1)
  33. expect(isReactive(obj.bar)).toBe(true)
  34. expect(obj.bar.baz).toBe(2)
  35. expect(isReactive(obj.arr)).toBe(true)
  36. expect(isReactive(obj.arr[0])).toBe(true)
  37. expect(obj.arr[0].foo).toBe(3)
  38. })
  39. it('ref value', () => {
  40. const r = ref({})
  41. expect(isReactive(r.value)).toBe(true)
  42. })
  43. it('should render', async () => {
  44. const app = new Vue({
  45. setup() {
  46. return {
  47. count: ref(42)
  48. }
  49. },
  50. render(this: any, h) {
  51. return h('div', this.count)
  52. }
  53. })
  54. const serverRenderer = createRenderer()
  55. const html = await serverRenderer.renderToString(app)
  56. expect(html).toBe('<div data-server-rendered="true">42</div>')
  57. })
  58. it('reactive + isReactive', () => {
  59. const state = reactive({})
  60. expect(isReactive(state)).toBe(true)
  61. })
  62. it('shallowRef + isRef', () => {
  63. const state = shallowRef({})
  64. expect(isRef(state)).toBe(true)
  65. })
  66. it('should work on objects sets with set()', () => {
  67. const state = ref<any>({})
  68. set(state.value, 'a', {})
  69. expect(isReactive(state.value.a)).toBe(true)
  70. set(state.value, 'a', {})
  71. expect(isReactive(state.value.a)).toBe(true)
  72. })
  73. it('should work on arrays sets with set()', () => {
  74. const state = ref<any>([])
  75. set(state.value, 1, {})
  76. expect(isReactive(state.value[1])).toBe(true)
  77. set(state.value, 1, {})
  78. expect(isReactive(state.value[1])).toBe(true)
  79. const rawArr = []
  80. set(rawArr, 1, {})
  81. expect(isReactive(rawArr[1])).toBe(false)
  82. })
  83. // #550
  84. it('props should work with set', async done => {
  85. let props: any
  86. const app = new Vue({
  87. render(this: any, h) {
  88. return h('child', { attrs: { msg: this.msg } })
  89. },
  90. setup() {
  91. return { msg: ref('hello') }
  92. },
  93. components: {
  94. child: {
  95. render(this: any, h: any) {
  96. return h('span', this.data.msg)
  97. },
  98. props: ['msg'],
  99. setup(_props) {
  100. props = _props
  101. return { data: _props }
  102. }
  103. }
  104. }
  105. })
  106. const serverRenderer = createRenderer()
  107. const html = await serverRenderer.renderToString(app)
  108. expect(html).toBe('<span data-server-rendered="true">hello</span>')
  109. expect(props.bar).toBeUndefined()
  110. set(props, 'bar', 'bar')
  111. expect(props.bar).toBe('bar')
  112. done()
  113. })
  114. // #721
  115. it('should behave correctly', () => {
  116. const state = ref({ old: ref(false) })
  117. set(state.value, 'new', ref(true))
  118. // console.log(process.server, 'state.value', JSON.stringify(state.value))
  119. expect(state.value).toMatchObject({
  120. old: false,
  121. new: true
  122. })
  123. })
  124. // #721
  125. it('should behave correctly for the nested ref in the object', () => {
  126. const state = { old: ref(false) }
  127. set(state, 'new', ref(true))
  128. expect(JSON.stringify(state)).toBe(
  129. '{"old":{"value":false},"new":{"value":true}}'
  130. )
  131. })
  132. // #721
  133. it('should behave correctly for ref of object', () => {
  134. const state = ref({ old: ref(false) })
  135. set(state.value, 'new', ref(true))
  136. expect(JSON.stringify(state.value)).toBe('{"old":false,"new":true}')
  137. })
  138. it('ssr should not RangeError: Maximum call stack size exceeded', async () => {
  139. new Vue({
  140. setup() {
  141. // @ts-expect-error
  142. const app = getCurrentInstance().proxy
  143. let mockNt: any = []
  144. mockNt.__ob__ = {}
  145. const test = reactive({
  146. app,
  147. mockNt
  148. })
  149. return {
  150. test
  151. }
  152. }
  153. })
  154. await nextTick()
  155. expect(
  156. `"RangeError: Maximum call stack size exceeded"`
  157. ).not.toHaveBeenWarned()
  158. })
  159. it('should work on objects sets with set()', () => {
  160. const state = ref<any>({})
  161. set(state.value, 'a', {})
  162. expect(isReactive(state.value.a)).toBe(true)
  163. })
  164. })