text: import Vue from '../../dist/vue.runtime.common.js'
  import VM from 'vm'
  import { createRenderer } from '../../packages/vue-server-renderer'
  const { renderToString } = createRenderer()
  
  describe('SSR: renderToString', () => {
    it('static attributes', done => {
      renderVmWithOptions({
        template: '<div id="foo" bar="123"></div>'
      }, result => {
        expect(result).toContain('<div id="foo" bar="123" data-server-rendered="true"></div>')
        done()
      })
    })
  
    it('unary tags', done => {
      renderVmWithOptions({
        template: '<input value="123">'
      }, result => {
        expect(result).toContain('<input value="123" data-server-rendered="true">')
        done()
      })
    })
  
    it('dynamic attributes', done => {
      renderVmWithOptions({
        template: '<div qux="quux" :id="foo" :bar="baz"></div>',
        data: {
          foo: 'hi',
          baz: 123
        }
      }, result => {
        expect(result).toContain('<div qux="quux" id="hi" bar="123" data-server-rendered="true"></div>')
        done()
      })
    })
  
    it('static class', done => {
      renderVmWithOptions({
        template: '<div class="foo bar"></div>'
      }, result => {
        expect(result).toContain('<div data-server-rendered="true" class="foo bar"></div>')
        done()
      })
    })
  
    it('dynamic class', done => {
      renderVmWithOptions({
        template: '<div class="foo bar" :class="[a, { qux: hasQux, quux: hasQuux }]"></div>',
        data: {
          a: 'baz',
          hasQux: true,
          hasQuux: false
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true" class="foo bar baz qux"></div>')
        done()
      })
    })
  
    it('custom component class', done => {
      renderVmWithOptions({
        template: '<div><cmp class="cmp"></cmp></div>',
        components: {
          cmp: {
            render: h => h('div', 'test')
          }
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><div class="cmp">test</div></div>')
        done()
      })
    })
  
    it('nested component class', done => {
      renderVmWithOptions({
        template: '<cmp class="outer" :class="cls"></cmp>',
        data: { cls: { 'success': 1 }},
        components: {
          cmp: {
            render: h => h('div', [h('nested', { staticClass: 'nested', 'class': { 'error': 1 }})]),
            components: {
              nested: {
                render: h => h('div', { staticClass: 'inner' }, 'test')
              }
            }
          }
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true" class="outer success">' +
            '<div class="inner nested error">test</div>' +
          '</div>')
        done()
      })
    })
  
    it('dynamic style', done => {
      renderVmWithOptions({
        template: '<div style="background-color:black" :style="{ fontSize: fontSize + \'px\', color: color }"></div>',
        data: {
          fontSize: 14,
          color: 'red'
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true" style="background-color:black;font-size:14px;color:red;"></div>'
        )
        done()
      })
    })
  
    it('dynamic string style', done => {
      renderVmWithOptions({
        template: '<div :style="style"></div>',
        data: {
          style: 'color:red'
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true" style="color:red;"></div>'
        )
        done()
      })
    })
  
    it('auto-prefixed style value as array', done => {
      renderVmWithOptions({
        template: '<div :style="style"></div>',
        data: {
          style: {
            display: ['-webkit-box', '-ms-flexbox', 'flex']
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true" style="display:-webkit-box;display:-ms-flexbox;display:flex;"></div>'
        )
        done()
      })
    })
  
    it('custom component style', done => {
      renderVmWithOptions({
        template: '<section><comp :style="style"></comp></section>',
        data: {
          style: 'color:red'
        },
        components: {
          comp: {
            template: '<div></div>'
          }
        }
      }, result => {
        expect(result).toContain(
          '<section data-server-rendered="true"><div style="color:red;"></div></section>'
        )
        done()
      })
    })
  
    it('nested custom component style', done => {
      renderVmWithOptions({
        template: '<comp style="color: blue" :style="style"></comp>',
        data: {
          style: 'color:red'
        },
        components: {
          comp: {
            template: '<nested style="text-align: left;" :style="{fontSize:\'520rem\'}"></nested>',
            components: {
              nested: {
                template: '<div></div>'
              }
            }
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true" style="text-align:left;font-size:520rem;color:red;"></div>'
        )
        done()
      })
    })
  
    it('component style not passed to child', done => {
      renderVmWithOptions({
        template: '<comp :style="style"></comp>',
        data: {
          style: 'color:red'
        },
        components: {
          comp: {
            template: '<div><div></div></div>'
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true" style="color:red;"><div></div></div>'
        )
        done()
      })
    })
  
    it('component style not passed to slot', done => {
      renderVmWithOptions({
        template: '<comp :style="style"><span style="color:black"></span></comp>',
        data: {
          style: 'color:red'
        },
        components: {
          comp: {
            template: '<div><slot></slot></div>'
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true" style="color:red;"><span style="color:black;"></span></div>'
        )
        done()
      })
    })
  
    it('attrs merging on components', done => {
      const Test = {
        render: h => h('div', {
          attrs: { id: 'a' }
        })
      }
      renderVmWithOptions({
        render: h => h(Test, {
          attrs: { id: 'b', name: 'c' }
        })
      }, res => {
        expect(res).toContain(
          '<div id="b" data-server-rendered="true" name="c"></div>'
        )
        done()
      })
    })
  
    it('domProps merging on components', done => {
      const Test = {
        render: h => h('div', {
          domProps: { innerHTML: 'a' }
        })
      }
      renderVmWithOptions({
        render: h => h(Test, {
          domProps: { innerHTML: 'b', value: 'c' }
        })
      }, res => {
        expect(res).toContain(
          '<div data-server-rendered="true" value="c">b</div>'
        )
        done()
      })
    })
  
    it('v-show directive render', done => {
      renderVmWithOptions({
        template: '<div v-show="false"><span>inner</span></div>'
      }, res => {
        expect(res).toContain(
          '<div data-server-rendered="true" style="display:none;"><span>inner</span></div>'
        )
        done()
      })
    })
  
    it('v-show directive merge with style', done => {
      renderVmWithOptions({
        template: '<div :style="[{lineHeight: 1}]" v-show="false"><span>inner</span></div>'
      }, res => {
        expect(res).toContain(
          '<div data-server-rendered="true" style="line-height:1;display:none;"><span>inner</span></div>'
        )
        done()
      })
    })
  
    it('v-show directive not passed to child', done => {
      renderVmWithOptions({
        template: '<foo v-show="false"></foo>',
        components: {
          foo: {
            template: '<div><span>inner</span></div>'
          }
        }
      }, res => {
        expect(res).toContain(
          '<div data-server-rendered="true" style="display:none;"><span>inner</span></div>'
        )
        done()
      })
    })
  
    it('v-show directive not passed to slot', done => {
      renderVmWithOptions({
        template: '<foo v-show="false"><span>inner</span></foo>',
        components: {
          foo: {
            template: '<div><slot></slot></div>'
          }
        }
      }, res => {
        expect(res).toContain(
          '<div data-server-rendered="true" style="display:none;"><span>inner</span></div>'
        )
        done()
      })
    })
  
    it('v-show directive merging on components', done => {
      renderVmWithOptions({
        template: '<foo v-show="false"></foo>',
        components: {
          foo: {
            render: h => h('bar', {
              directives: [{
                name: 'show',
                value: true
              }]
            }),
            components: {
              bar: {
                render: h => h('div', 'inner')
              }
            }
          }
        }
      }, res => {
        expect(res).toContain(
          '<div data-server-rendered="true" style="display:none;">inner</div>'
        )
        done()
      })
    })
  
    it('text interpolation', done => {
      renderVmWithOptions({
        template: '<div>{{ foo }} side {{ bar }}</div>',
        data: {
          foo: 'server',
          bar: '<span>rendering</span>'
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true">server side &lt;span&gt;rendering&lt;/span&gt;</div>')
        done()
      })
    })
  
    it('v-html on root', done => {
      renderVmWithOptions({
        template: '<div v-html="text"></div>',
        data: {
          text: '<span>foo</span>'
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><span>foo</span></div>')
        done()
      })
    })
  
    it('v-text on root', done => {
      renderVmWithOptions({
        template: '<div v-text="text"></div>',
        data: {
          text: '<span>foo</span>'
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true">&lt;span&gt;foo&lt;/span&gt;</div>')
        done()
      })
    })
  
    it('v-html', done => {
      renderVmWithOptions({
        template: '<div><div v-html="text"></div></div>',
        data: {
          text: '<span>foo</span>'
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><div><span>foo</span></div></div>')
        done()
      })
    })
  
    it('v-html with null value', done => {
      renderVmWithOptions({
        template: '<div><div v-html="text"></div></div>',
        data: {
          text: null
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><div></div></div>')
        done()
      })
    })
  
    it('v-text', done => {
      renderVmWithOptions({
        template: '<div><div v-text="text"></div></div>',
        data: {
          text: '<span>foo</span>'
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><div>&lt;span&gt;foo&lt;/span&gt;</div></div>')
        done()
      })
    })
  
    it('v-text with null value', done => {
      renderVmWithOptions({
        template: '<div><div v-text="text"></div></div>',
        data: {
          text: null
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><div></div></div>')
        done()
      })
    })
  
    it('child component (hoc)', done => {
      renderVmWithOptions({
        template: '<child class="foo" :msg="msg"></child>',
        data: {
          msg: 'hello'
        },
        components: {
          child: {
            props: ['msg'],
            data () {
              return { name: 'bar' }
            },
            render () {
              const h = this.$createElement
              return h('div', { class: ['bar'] }, [`${this.msg} ${this.name}`])
            }
          }
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true" class="foo bar">hello bar</div>')
        done()
      })
    })
  
    it('has correct lifecycle during render', done => {
      let lifecycleCount = 1
      renderVmWithOptions({
        template: '<div><span>{{ val }}</span><test></test></div>',
        data: {
          val: 'hi'
        },
        beforeCreate () {
          expect(lifecycleCount++).toBe(1)
        },
        created () {
          this.val = 'hello'
          expect(this.val).toBe('hello')
          expect(lifecycleCount++).toBe(2)
        },
        components: {
          test: {
            beforeCreate () {
              expect(lifecycleCount++).toBe(3)
            },
            created () {
              expect(lifecycleCount++).toBe(4)
            },
            render () {
              expect(lifecycleCount++).toBeGreaterThan(4)
              return this.$createElement('span', { class: ['b'] }, 'testAsync')
            }
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true">' +
            '<span>hello</span>' +
            '<span class="b">testAsync</span>' +
          '</div>'
        )
        done()
      })
    })
  
    it('computed properties', done => {
      renderVmWithOptions({
        template: '<div>{{ b }}</div>',
        data: {
          a: {
            b: 1
          }
        },
        computed: {
          b () {
            return this.a.b + 1
          }
        },
        created () {
          this.a.b = 2
          expect(this.b).toBe(3)
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true">3</div>')
        done()
      })
    })
  
    it('renders async component', done => {
      renderVmWithOptions({
        template: `
          <div>
            <test-async></test-async>
          </div>
        `,
        components: {
          testAsync (resolve) {
            setTimeout(() => resolve({
              render () {
                return this.$createElement('span', { class: ['b'] }, 'testAsync')
              }
            }), 1)
          }
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><span class="b">testAsync</span></div>')
        done()
      })
    })
  
    it('renders async component (Promise, nested)', done => {
      const Foo = () => Promise.resolve({
        render: h => h('div', [h('span', 'foo'), h(Bar)])
      })
      const Bar = () => ({
        component: Promise.resolve({
          render: h => h('span', 'bar')
        })
      })
      renderVmWithOptions({
        render: h => h(Foo)
      }, res => {
        expect(res).toContain(`<div data-server-rendered="true"><span>foo</span><span>bar</span></div>`)
        done()
      })
    })
  
    it('renders async component (ES module)', done => {
      const Foo = () => Promise.resolve({
        __esModule: true,
        default: {
          render: h => h('div', [h('span', 'foo'), h(Bar)])
        }
      })
      const Bar = () => ({
        component: Promise.resolve({
          __esModule: true,
          default: {
            render: h => h('span', 'bar')
          }
        })
      })
      renderVmWithOptions({
        render: h => h(Foo)
      }, res => {
        expect(res).toContain(`<div data-server-rendered="true"><span>foo</span><span>bar</span></div>`)
        done()
      })
    })
  
    it('renders async component (hoc)', done => {
      renderVmWithOptions({
        template: '<test-async></test-async>',
        components: {
          testAsync: () => Promise.resolve({
            render () {
              return this.$createElement('span', { class: ['b'] }, 'testAsync')
            }
          })
        }
      }, result => {
        expect(result).toContain('<span data-server-rendered="true" class="b">testAsync</span>')
        done()
      })
    })
  
    it('renders async component (functional, single node)', done => {
      renderVmWithOptions({
        template: `
          <div>
            <test-async></test-async>
          </div>
        `,
        components: {
          testAsync (resolve) {
            setTimeout(() => resolve({
              functional: true,
              render (h) {
                return h('span', { class: ['b'] }, 'testAsync')
              }
            }), 1)
          }
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><span class="b">testAsync</span></div>')
        done()
      })
    })
  
    it('renders async component (functional, multiple nodes)', done => {
      renderVmWithOptions({
        template: `
          <div>
            <test-async></test-async>
          </div>
        `,
        components: {
          testAsync (resolve) {
            setTimeout(() => resolve({
              functional: true,
              render (h) {
                return [
                  h('span', { class: ['a'] }, 'foo'),
                  h('span', { class: ['b'] }, 'bar')
                ]
              }
            }), 1)
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true">' +
            '<span class="a">foo</span>' +
            '<span class="b">bar</span>' +
          '</div>'
        )
        done()
      })
    })
  
    it('renders nested async functional component', done => {
      renderVmWithOptions({
        template: `
          <div>
            <outer-async></outer-async>
          </div>
        `,
        components: {
          outerAsync (resolve) {
            setTimeout(() => resolve({
              functional: true,
              render (h) {
                return h('innerAsync')
              }
            }), 1)
          },
          innerAsync (resolve) {
            setTimeout(() => resolve({
              functional: true,
              render (h) {
                return h('span', { class: ['a'] }, 'inner')
              },
            }), 1)
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true">' +
            '<span class="a">inner</span>' +
          '</div>'
        )
        done()
      })
    })
  
    it('should catch async component error', done => {
      Vue.config.silent = true
      renderToString(new Vue({
        template: '<test-async></test-async>',
        components: {
          testAsync: () => Promise.resolve({
            render () {
              throw new Error('foo')
            }
          })
        }
      }), (err, result) => {
        Vue.config.silent = false
        expect(err).toBeTruthy()
        expect(result).toBeUndefined()
        done()
      })
    })
  
    // #11963, #10391
    it('renders async children passed in slots', done => {
      const Parent = {
        template: `<div><slot name="child"/></div>`
      }
      const Child = {
        template: `<p>child</p>`
      }
      renderVmWithOptions({
        template: `
        <Parent>
          <template #child>
            <Child/>
          </template>
        </Parent>
        `,
        components: {
          Parent,
          Child: () => Promise.resolve(Child)
        }
      }, result => {
        expect(result).toContain(
          `<div data-server-rendered="true"><p>child</p></div>`
        )
        done()
      })
    })
  
    it('everything together', done => {
      renderVmWithOptions({
        template: `
          <div>
            <p class="hi">yoyo</p>
            <div id="ho" :class="{ red: isRed }"></div>
            <span>{{ test }}</span>
            <input :value="test">
            <img :src="imageUrl">
            <test></test>
            <test-async></test-async>
          </div>
        `,
        data: {
          test: 'hi',
          isRed: true,
          imageUrl: 'https://vuejs.org/images/logo.png'
        },
        components: {
          test: {
            render () {
              return this.$createElement('div', { class: ['a'] }, 'test')
            }
          },
          testAsync (resolve) {
            resolve({
              render () {
                return this.$createElement('span', { class: ['b'] }, 'testAsync')
              }
            })
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true">' +
            '<p class="hi">yoyo</p> ' +
            '<div id="ho" class="red"></div> ' +
            '<span>hi</span> ' +
            '<input value="hi"> ' +
            '<img src="https://vuejs.org/images/logo.png"> ' +
            '<div class="a">test</div> ' +
            '<span class="b">testAsync</span>' +
          '</div>'
        )
        done()
      })
    })
  
    it('normal attr', done => {
      renderVmWithOptions({
        template: `
          <div>
            <span :test="'ok'">hello</span>
            <span :test="null">hello</span>
            <span :test="false">hello</span>
            <span :test="true">hello</span>
            <span :test="0">hello</span>
          </div>
        `
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true">' +
            '<span test="ok">hello</span> ' +
            '<span>hello</span> ' +
            '<span>hello</span> ' +
            '<span test="true">hello</span> ' +
            '<span test="0">hello</span>' +
          '</div>'
        )
        done()
      })
    })
  
    it('enumerated attr', done => {
      renderVmWithOptions({
        template: `
          <div>
            <span :draggable="true">hello</span>
            <span :draggable="'ok'">hello</span>
            <span :draggable="null">hello</span>
            <span :draggable="false">hello</span>
            <span :draggable="''">hello</span>
            <span :draggable="'false'">hello</span>
          </div>
        `
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true">' +
            '<span draggable="true">hello</span> ' +
            '<span draggable="true">hello</span> ' +
            '<span draggable="false">hello</span> ' +
            '<span draggable="false">hello</span> ' +
            '<span draggable="true">hello</span> ' +
            '<span draggable="false">hello</span>' +
          '</div>'
        )
        done()
      })
    })
  
    it('boolean attr', done => {
      renderVmWithOptions({
        template: `
          <div>
            <span :disabled="true">hello</span>
            <span :disabled="'ok'">hello</span>
            <span :disabled="null">hello</span>
            <span :disabled="''">hello</span>
          </div>
        `
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true">' +
            '<span disabled="disabled">hello</span> ' +
            '<span disabled="disabled">hello</span> ' +
            '<span>hello</span> ' +
            '<span disabled="disabled">hello</span>' +
          '</div>'
        )
        done()
      })
    })
  
    it('v-bind object', done => {
      renderVmWithOptions({
        data: {
          test: { id: 'a', class: ['a', 'b'], value: 'c' }
        },
        template: '<input v-bind="test">'
      }, result => {
        expect(result).toContain('<input id="a" data-server-rendered="true" value="c" class="a b">')
        done()
      })
    })
  
    it('custom directives', done => {
      const renderer = createRenderer({
        directives: {
          'class-prefixer': (node, dir) => {
            if (node.data.class) {
              node.data.class = `${dir.value}-${node.data.class}`
            }
            if (node.data.staticClass) {
              node.data.staticClass = `${dir.value}-${node.data.staticClass}`
            }
          }
        }
      })
      renderer.renderToString(new Vue({
        render () {
          const h = this.$createElement
          return h('p', {
            class: 'class1',
            staticClass: 'class2',
            directives: [{
              name: 'class-prefixer',
              value: 'my'
            }]
          }, ['hello world'])
        }
      }), (err, result) => {
        expect(err).toBeNull()
        expect(result).toContain('<p data-server-rendered="true" class="my-class2 my-class1">hello world</p>')
        done()
      })
    })
  
    it('should not warn for custom directives that do not have server-side implementation', done => {
      renderToString(new Vue({
        directives: {
          test: {
            bind() {
              // noop
            }
          }
        },
        template: '<div v-test></div>',
      }), () => {
        expect('Failed to resolve directive: test').not.toHaveBeenWarned()
        done()
      })
    })
  
    it('_scopeId', done => {
      renderVmWithOptions({
        _scopeId: '_v-parent',
        template: '<div id="foo"><p><child></child></p></div>',
        components: {
          child: {
            _scopeId: '_v-child',
            render () {
              const h = this.$createElement
              return h('div', null, [h('span', null, ['foo'])])
            }
          }
        }
      }, result => {
        expect(result).toContain(
          '<div id="foo" data-server-rendered="true" _v-parent>' +
            '<p _v-parent>' +
              '<div _v-child _v-parent><span _v-child>foo</span></div>' +
            '</p>' +
          '</div>'
        )
        done()
      })
    })
  
    it('_scopeId on slot content', done => {
      renderVmWithOptions({
        _scopeId: '_v-parent',
        template: '<div><child><p>foo</p></child></div>',
        components: {
          child: {
            _scopeId: '_v-child',
            render () {
              const h = this.$createElement
              return h('div', null, this.$slots.default)
            }
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true" _v-parent>' +
            '<div _v-child _v-parent><p _v-child _v-parent>foo</p></div>' +
          '</div>'
        )
        done()
      })
    })
  
    it('comment nodes', done => {
      renderVmWithOptions({
        template: '<div><transition><div v-if="false"></div></transition></div>'
      }, result => {
        expect(result).toContain(`<div data-server-rendered="true"><!----></div>`)
        done()
      })
    })
  
    it('should catch error', done => {
      Vue.config.silent = true
      renderToString(new Vue({
        render () {
          throw new Error('oops')
        }
      }), err => {
        expect(err instanceof Error).toBe(true)
        Vue.config.silent = false
        done()
      })
    })
  
    it('default value Foreign Function', () => {
      const FunctionConstructor = VM.runInNewContext('Function')
      const func = () => 123
      const vm = new Vue({
        props: {
          a: {
            type: FunctionConstructor,
            default: func
          }
        },
        propsData: {
          a: undefined
        }
      })
      expect(vm.a).toBe(func)
    })
  
    it('should prevent xss in attributes', done => {
      renderVmWithOptions({
        data: {
          xss: '"><script>alert(1)</script>'
        },
        template: `
          <div>
            <a :title="xss" :style="{ color: xss }" :class="[xss]">foo</a>
          </div>
        `
      }, res => {
        expect(res).not.toContain(`<script>alert(1)</script>`)
        done()
      })
    })
  
    it('should prevent xss in attribute names', done => {
      renderVmWithOptions({
        data: {
          xss: {
            'foo="bar"></div><script>alert(1)</script>': ''
          }
        },
        template: `
          <div v-bind="xss"></div>
        `
      }, res => {
        expect(res).not.toContain(`<script>alert(1)</script>`)
        done()
      })
    })
  
    it('should prevent xss in attribute names (optimized)', done => {
      renderVmWithOptions({
        data: {
          xss: {
            'foo="bar"></div><script>alert(1)</script>': ''
          }
        },
        template: `
          <div>
            <a v-bind="xss">foo</a>
          </div>
        `
      }, res => {
        expect(res).not.toContain(`<script>alert(1)</script>`)
        done()
      })
    })
  
    it('should prevent script xss with v-bind object syntax + array value', done => {
      renderVmWithOptions({
        data: {
          test: ['"><script>alert(1)</script><!--"']
        },
        template: `<div v-bind="{ test }"></div>`
      }, res => {
        expect(res).not.toContain(`<script>alert(1)</script>`)
        done()
      })
    })
  
    it('v-if', done => {
      renderVmWithOptions({
        template: `
          <div>
            <span v-if="true">foo</span>
            <span v-if="false">bar</span>
          </div>
        `
      }, res => {
        expect(res).toContain(`<div data-server-rendered="true"><span>foo</span> <!----></div>`)
        done()
      })
    })
  
    it('v-for', done => {
      renderVmWithOptions({
        template: `
          <div>
            <span>foo</span>
            <span v-for="i in 2">{{ i }}</span>
          </div>
        `
      }, res => {
        expect(res).toContain(`<div data-server-rendered="true"><span>foo</span> <span>1</span><span>2</span></div>`)
        done()
      })
    })
  
    it('template v-if', done => {
      renderVmWithOptions({
        template: `
          <div>
            <span>foo</span>
            <template v-if="true">
              <span>foo</span> bar <span>baz</span>
            </template>
          </div>
        `
      }, res => {
        expect(res).toContain(`<div data-server-rendered="true"><span>foo</span> <span>foo</span> bar <span>baz</span></div>`)
        done()
      })
    })
  
    it('template v-for', done => {
      renderVmWithOptions({
        template: `
          <div>
            <span>foo</span>
            <template v-for="i in 2">
              <span>{{ i }}</span><span>bar</span>
            </template>
          </div>
        `
      }, res => {
        expect(res).toContain(`<div data-server-rendered="true"><span>foo</span> <span>1</span><span>bar</span><span>2</span><span>bar</span></div>`)
        done()
      })
    })
  
    it('with inheritAttrs: false + $attrs', done => {
      renderVmWithOptions({
        template: `<foo id="a"/>`,
        components: {
          foo: {
            inheritAttrs: false,
            template: `<div><div v-bind="$attrs"></div></div>`
          }
        }
      }, res => {
        expect(res).toBe(`<div data-server-rendered="true"><div id="a"></div></div>`)
        done()
      })
    })
  
    it('should escape static strings', done => {
      renderVmWithOptions({
        template: `<div>&lt;foo&gt;</div>`
      }, res => {
        expect(res).toBe(`<div data-server-rendered="true">&lt;foo&gt;</div>`)
        done()
      })
    })
  
    it('should not cache computed properties', done => {
      renderVmWithOptions({
        template: `<div>{{ foo }}</div>`,
        data: () => ({ bar: 1 }),
        computed: {
          foo () { return this.bar + 1 }
        },
        created () {
          this.foo // access
          this.bar++ // trigger change
        }
      }, res => {
        expect(res).toBe(`<div data-server-rendered="true">3</div>`)
        done()
      })
    })
  
    // #8977
    it('should call computed properties with vm as first argument', done => {
      renderToString(new Vue({
        data: {
          firstName: 'Evan',
          lastName: 'You'
        },
        computed: {
          fullName: ({ firstName, lastName }) => `${firstName} ${lastName}`,
        },
        template: '<div>{{ fullName }}</div>',
      }), (err, result) => {
        expect(err).toBeNull()
        expect(result).toContain('<div data-server-rendered="true">Evan You</div>')
        done()
      })
    })
  
    it('return Promise', done => {
      renderToString(new Vue({
        template: `<div>{{ foo }}</div>`,
        data: { foo: 'bar' }
      })).then(res => {
        expect(res).toBe(`<div data-server-rendered="true">bar</div>`)
        done()
      })
    })
  
    it('return Promise (error)', done => {
      Vue.config.silent = true
      renderToString(new Vue({
        render () {
          throw new Error('foobar')
        }
      })).catch(err => {
        expect(err.toString()).toContain(`foobar`)
        Vue.config.silent = false
        done()
      })
    })
  
    it('should catch template compilation error', done => {
      renderToString(new Vue({
        template: `<div></div><div></div>`
      }), (err) => {
        expect(err.toString()).toContain('Component template should contain exactly one root element')
        done()
      })
    })
  
    // #6907
    it('should not optimize root if conditions', done => {
      renderVmWithOptions({
        data: { foo: 123 },
        template: `<input :type="'text'" v-model="foo">`
      }, res => {
        expect(res).toBe(`<input type="text" data-server-rendered="true" value="123">`)
        done()
      })
    })
  
    it('render muted properly', done => {
      renderVmWithOptions({
        template: '<video muted></video>'
      }, result => {
        expect(result).toContain('<video muted="muted" data-server-rendered="true"></video>')
        done()
      })
    })
  
    it('render v-model with textarea', done => {
      renderVmWithOptions({
        data: { foo: 'bar' },
        template: '<div><textarea v-model="foo"></textarea></div>'
      }, result => {
        expect(result).toContain('<textarea>bar</textarea>')
        done()
      })
    })
  
    it('render v-model with textarea (non-optimized)', done => {
      renderVmWithOptions({
        render (h) {
          return h('textarea', {
            domProps: {
              value: 'foo'
            }
          })
        }
      }, result => {
        expect(result).toContain('<textarea data-server-rendered="true">foo</textarea>')
        done()
      })
    })
  
    it('render v-model with <select> (value binding)', done => {
      renderVmWithOptions({
        data: {
          selected: 2,
          options: [
            { id: 1, label: 'one' },
            { id: 2, label: 'two' }
          ]
        },
        template: `
        <div>
          <select v-model="selected">
            <option v-for="o in options" :value="o.id">{{ o.label }}</option>
          </select>
        </div>
        `
      }, result => {
        expect(result).toContain(
          '<select>' +
            '<option value="1">one</option>' +
            '<option selected="selected" value="2">two</option>' +
          '</select>'
        )
        done()
      })
    })
  
    it('render v-model with <select> (static value)', done => {
      renderVmWithOptions({
        data: {
          selected: 2
        },
        template: `
        <div>
          <select v-model="selected">
            <option value="1">one</option>
            <option value="2">two</option>
          </select>
        </div>
        `
      }, result => {
        expect(result).toContain(
          '<select>' +
            '<option value="1">one</option> ' +
            '<option value="2" selected="selected">two</option>' +
          '</select>'
        )
        done()
      })
    })
  
    it('render v-model with <select> (text as value)', done => {
      renderVmWithOptions({
        data: {
          selected: 2,
          options: [
            { id: 1, label: 'one' },
            { id: 2, label: 'two' }
          ]
        },
        template: `
        <div>
          <select v-model="selected">
            <option v-for="o in options">{{ o.id }}</option>
          </select>
        </div>
        `
      }, result => {
        expect(result).toContain(
          '<select>' +
            '<option>1</option>' +
            '<option selected="selected">2</option>' +
          '</select>'
        )
        done()
      })
    })
  
    // #7223
    it('should not double escape attribute values', done => {
      renderVmWithOptions({
        template: `
        <div>
          <div id="a\nb"></div>
        </div>
        `
      }, result => {
        expect(result).toContain(`<div id="a\nb"></div>`)
        done()
      })
    })
  
    // #7859
    it('should not double escape class values', done => {
      renderVmWithOptions({
        template: `
        <div>
          <div class="a\nb"></div>
        </div>
        `
      }, result => {
        expect(result).toContain(`<div class="a b"></div>`)
        done()
      })
    })
  
    it('should expose ssr helpers on functional context', done => {
      let called = false
      renderVmWithOptions({
        template: `<div><foo/></div>`,
        components: {
          foo: {
            functional: true,
            render (h, ctx) {
              expect(ctx._ssrNode).toBeTruthy()
              called = true
            }
          }
        }
      }, () => {
        expect(called).toBe(true)
        done()
      })
    })
  
    it('should support serverPrefetch option', done => {
      renderVmWithOptions({
        template: `
          <div>{{ count }}</div>
        `,
        data: {
          count: 0
        },
        serverPrefetch () {
          return new Promise((resolve) => {
            setTimeout(() => {
              this.count = 42
              resolve()
            }, 1)
          })
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true">42</div>')
        done()
      })
    })
  
    it('should support serverPrefetch option (nested)', done => {
      renderVmWithOptions({
        template: `
          <div>
            <span>{{ count }}</span>
            <nested-prefetch></nested-prefetch>
          </div>
        `,
        data: {
          count: 0
        },
        serverPrefetch () {
          return new Promise((resolve) => {
            setTimeout(() => {
              this.count = 42
              resolve()
            }, 1)
          })
        },
        components: {
          nestedPrefetch: {
            template: `
              <div>{{ message }}</div>
            `,
            data () {
              return {
                message: ''
              }
            },
            serverPrefetch () {
              return new Promise((resolve) => {
                setTimeout(() => {
                  this.message = 'vue.js'
                  resolve()
                }, 1)
              })
            }
          }
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><span>42</span> <div>vue.js</div></div>')
        done()
      })
    })
  
    it('should support serverPrefetch option (nested async)', done => {
      renderVmWithOptions({
        template: `
          <div>
            <span>{{ count }}</span>
            <nested-prefetch></nested-prefetch>
          </div>
        `,
        data: {
          count: 0
        },
        serverPrefetch () {
          return new Promise((resolve) => {
            setTimeout(() => {
              this.count = 42
              resolve()
            }, 1)
          })
        },
        components: {
          nestedPrefetch (resolve) {
            resolve({
              template: `
                <div>{{ message }}</div>
              `,
              data () {
                return {
                  message: ''
                }
              },
              serverPrefetch () {
                return new Promise((resolve) => {
                  setTimeout(() => {
                    this.message = 'vue.js'
                    resolve()
                  }, 1)
                })
              }
            })
          }
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><span>42</span> <div>vue.js</div></div>')
        done()
      })
    })
  
    it('should merge serverPrefetch option', done => {
      const mixin = {
        data: {
          message: ''
        },
        serverPrefetch () {
          return new Promise((resolve) => {
            setTimeout(() => {
              this.message = 'vue.js'
              resolve()
            }, 1)
          })
        }
      }
      renderVmWithOptions({
        mixins: [mixin],
        template: `
          <div>
            <span>{{ count }}</span>
            <div>{{ message }}</div>
          </div>
        `,
        data: {
          count: 0
        },
        serverPrefetch () {
          return new Promise((resolve) => {
            setTimeout(() => {
              this.count = 42
              resolve()
            }, 1)
          })
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><span>42</span> <div>vue.js</div></div>')
        done()
      })
    })
  
    it(`should skip serverPrefetch option that doesn't return a promise`, done => {
      renderVmWithOptions({
        template: `
          <div>{{ count }}</div>
        `,
        data: {
          count: 0
        },
        serverPrefetch () {
          setTimeout(() => {
            this.count = 42
          }, 1)
        }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true">0</div>')
        done()
      })
    })
  
    it('should call context.rendered', done => {
      let a = 0
      renderToString(new Vue({
        template: '<div>Hello</div>'
      }), {
        rendered: () => {
          a = 42
        }
      }, (err, res) => {
        expect(err).toBeNull()
        expect(res).toContain('<div data-server-rendered="true">Hello</div>')
        expect(a).toBe(42)
        done()
      })
    })
  
    it('invalid style value', done => {
      renderVmWithOptions({
        template: '<div :style="style"><p :style="style2"/></div>',
        data: {
          // all invalid, should not even have "style" attribute
          style: {
            opacity: {},
            color: null
          },
          // mix of valid and invalid
          style2: {
            opacity: 0,
            color: null
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true"><p style="opacity:0;"></p></div>'
        )
        done()
      })
    })
  
    it('numeric style value', done => {
      renderVmWithOptions({
        template: '<div :style="style"></div>',
        data: {
          style: {
            opacity: 0, // valid, opacity is unit-less
            top: 0, // valid, top requires unit but 0 is allowed
            left: 10, // invalid, left requires a unit
            marginTop: '10px' // valid
          }
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true" style="opacity:0;top:0;margin-top:10px;"></div>'
        )
        done()
      })
    })
  
    it('handling max stack size limit', done => {
      const vueInstance = new Vue({
        template: `<div class="root">
          <child v-for="(x, i) in items" :key="i"></child>
        </div>`,
        components: {
          child: {
            template: '<div class="child"><span class="child">hi</span></div>'
          }
        },
        data: {
          items: Array(1000).fill(0)
        }
      })
  
      renderToString(vueInstance, err => done(err))
    })
  
    it('undefined v-model with textarea', done => {
      renderVmWithOptions({
        render (h) {
          return h('div', [
            h('textarea', {
              domProps: {
                value: null
              }
            })
          ])
        }
      }, result => {
        expect(result).toContain(
          '<div data-server-rendered="true"><textarea></textarea></div>'
        )
        done()
      })
    })
  
    it('Options inheritAttrs in parent component', done => {
      const childComponent = {
        template: `<div>{{ someProp }}</div>`,
        props: {
          someProp: {}
        },
      }
      const parentComponent = {
        template: `<childComponent v-bind="$attrs" />`,
        components: { childComponent },
        inheritAttrs: false
      }
      renderVmWithOptions({
        template: `
          <div>
            <parentComponent some-prop="some-val" />
          </div>
          `,
        components: { parentComponent }
      }, result => {
        expect(result).toContain('<div data-server-rendered="true"><div>some-val</div></div>')
        done()
      })
    })
  })
  
  it('only static style and not v-show ', done => {
    renderVmWithOptions({
      template: `    <div
      style="
      margin-top:
      0px;width: 24px;
      height: 16px;
      vertical-align: text-bottom;
      fill: #bfbfc3;"
    ></div>`,
    }, result => {
      expect(result).toContain(
        '<div data-server-rendered="true" style="width:24px;height:16px;vertical-align:text-bottom;fill:#bfbfc3;"></div>'
      )
      done()
    })
  })
  
  function renderVmWithOptions (options, cb) {
    renderToString(new Vue(options), (err, res) => {
      expect(err).toBeNull()
      cb(res)
    })
  }
  