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

feat: renderToString implementation #1572

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/index.ts
@@ -1,7 +1,7 @@
import { DOMWrapper } from './domWrapper'
import { VueWrapper } from './vueWrapper'
import BaseWrapper from './baseWrapper'
import { mount, shallowMount } from './mount'
import { mount, shallowMount, renderToString } from './mount'
import { MountingOptions } from './types'
import { RouterLinkStub } from './components/RouterLinkStub'
import { createWrapperError } from './errorWrapper'
Expand All @@ -12,6 +12,7 @@ import { enableAutoUnmount, disableAutoUnmount } from './utils/autoUnmount'
export {
mount,
shallowMount,
renderToString,
enableAutoUnmount,
disableAutoUnmount,
RouterLinkStub,
Expand Down
12 changes: 11 additions & 1 deletion src/mount.ts
Expand Up @@ -26,6 +26,7 @@ import {
ConcreteComponent,
Prop
} from 'vue'
import { renderToString as baseRenderToString } from 'vue/server-renderer'

import { MountingOptions, Slot } from './types'
import {
Expand Down Expand Up @@ -54,7 +55,8 @@ const MOUNT_OPTIONS: Array<keyof MountingOptions<any>> = [
'props',
'slots',
'global',
'shallow'
'shallow',
'renderToString'
]

function getInstanceOptions(
Expand Down Expand Up @@ -516,6 +518,10 @@ export function mount(
}
}

if (options?.renderToString) {
return baseRenderToString(app) as any
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be Promise<string>?

}

// mount the app!
const vm = app.mount(el)

Expand All @@ -538,3 +544,7 @@ export function mount(
export const shallowMount: typeof mount = (component: any, options?: any) => {
return mount(component, { ...options, shallow: true })
}

export const renderToString: typeof mount = (component: any, options?: any) => {
return mount(component, { ...options, renderToString: true })
}
6 changes: 6 additions & 0 deletions src/types.ts
Expand Up @@ -84,6 +84,12 @@ export interface MountingOptions<Props, Data = {}> {
* @see https://next.vue-test-utils.vuejs.org/api/#slots
*/
shallow?: boolean
/**
* Renders a component to HTML.
* @default false
* @see https://next.vue-test-utils.vuejs.org/api/#renderToString
*/
renderToString?: boolean
}

export type Stub = boolean | Component
Expand Down
55 changes: 55 additions & 0 deletions tests/renderToString.spec.ts
@@ -0,0 +1,55 @@
import { defineComponent, onMounted, onServerPrefetch, ref } from 'vue'
import { renderToString } from '../src'

describe('renderToString', () => {
it('returns a promise', async () => {
const Component = defineComponent({
template: '<div>{{ text }}</div>',
setup() {
return { text: 'Text content' }
}
})

const wrapper = await renderToString(Component)

expect(wrapper).toBe('<div>Text content</div>')
})

it('returns correct html on multi root nodes', async () => {
const Component = defineComponent({
template: '<div>foo</div><div>bar</div>'
})

const wrapper = await renderToString(Component)

expect(wrapper).toBe('<!--[--><div>foo</div><div>bar</div><!--]-->')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should use the matchInlineSnapshot matcher?

})

it('returns correct html when onServerPrefetch is used', async () => {
const Component = defineComponent({
template: '<div>{{ text }}</div>',
setup() {
const text = ref('')

onServerPrefetch(() => {
return new Promise((resolve) => {
setTimeout(() => {
text.value = 'Text content'
resolve(true)
}, 100)
})
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onServerPrefetch sets the text to Text content, and onMounted as well: which one is tested in this test? Maybe they should have different values, and we should check both in the test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I can update this part to check if the rendered text is the text from pre-fetched data? Something like

it('returns correct html with pre-fetched data on server', async () => {
  function fakeFetch(text: string) {
    return new Promise<string>((resolve) => {
      setTimeout(() => {
        resolve(text)
      }, 100)
    })
  }

  const Component = defineComponent({
    template: '<div>{{ text }}</div>',
    setup() {
      const text = ref<string | null>(null)

      onServerPrefetch(async () => {
        text.value = await fakeFetch('onServerPrefetch')
      })

      onMounted(async () => {
        if (!text.value) {
          text.value = await fakeFetch('onMounted')
        }
      })

      return { text }
    }
  })

  const contents = await renderToString(Component)

  expect(contents).toBe('<div>onServerPrefetch</div>')
})

Basing on this one https://vuejs.org/api/composition-api-lifecycle.html#onserverprefetch

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think that would be great 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated 👍🏼


onMounted(() => {
text.value = 'Text content'
})

return { text }
}
})

const wrapper = await renderToString(Component)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

won't it be confusing to have renderToString both in server-renderer and in VTU?
Maybe we could call it otherwise in VTU?


expect(wrapper).toBe('<div>Text content</div>')
})
})