import {
  compileAndStringify,
  prepareRuntime,
  resetRuntime,
  createInstance,
  syncPromise,
  checkRefresh
} from '../helpers/index'

describe('node in render function', () => {
  let runtime

  beforeAll(() => {
    runtime = prepareRuntime()
  })

  afterAll(() => {
    resetRuntime()
    runtime = null
  })

  it('should be generated', () => {
    const instance = createInstance(runtime, `
      new Vue({
        render: function (createElement) {
          return createElement('div', {}, [
            createElement('text', { attrs: { value: 'Hello' }}, [])
          ])
        },
        el: "body"
      })
    `)
    expect(instance.getRealRoot()).toEqual({
      type: 'div',
      children: [
        { type: 'text', attr: { value: 'Hello' }}
      ]
    })
  })

  it('should be generated with all types of text', () => {
    const instance = createInstance(runtime, `
      new Vue({
        render: function (createElement) {
          return createElement('div', {}, [
            createElement('text', { attrs: { value: 'Hello' }}, []),
            'World',
            createElement('text', {}, ['Weex'])
          ])
        },
        el: "body"
      })
    `)
    expect(instance.getRealRoot()).toEqual({
      type: 'div',
      children: [
        { type: 'text', attr: { value: 'Hello' }},
        { type: 'text', attr: { value: 'World' }},
        { type: 'text', attr: { value: 'Weex' }}
      ]
    })
  })

  it('should be generated with comments', () => {
    // todo
  })

  it('should be generated with module diff', (done) => {
    const instance = createInstance(runtime, `
      new Vue({
        data: {
          counter: 0
        },
        methods: {
          foo: function () {}
        },
        render: function (createElement) {
          switch (this.counter) {
            case 1:
            return createElement('div', {}, [
              createElement('text', { attrs: { value: 'World' }}, [])
            ])

            case 2:
            return createElement('div', {}, [
              createElement('text', { attrs: { value: 'World' }, style: { fontSize: 100 }}, [])
            ])

            case 3:
            return createElement('div', {}, [
              createElement('text', {
                attrs: { value: 'World' },
                style: { fontSize: 100 },
                on: { click: this.foo }
              }, [])
            ])

            case 4:
            return createElement('div', {}, [
              createElement('text', {
                attrs: { value: 'Weex' },
                style: { color: '#ff0000' }
              }, [])
            ])

            default:
            return createElement('div', {}, [
              createElement('text', { attrs: { value: 'Hello' }}, [])
            ])
          }
        },
        el: "body"
      })
    `)
    expect(instance.getRealRoot()).toEqual({
      type: 'div',
      children: [
        { type: 'text', attr: { value: 'Hello' }}
      ]
    })

    syncPromise([
      checkRefresh(instance, { counter: 1 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'World' }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 2 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'World' }, style: { fontSize: 100 }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 3 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'World' }, style: { fontSize: 100 }, event: ['click'] }
          ]
        })
      }),
      checkRefresh(instance, { counter: 4 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Weex' }, style: { fontSize: '', color: '#ff0000' }}
          ]
        })
        done()
      })
    ])
  })

  it('should be generated with sub components', () => {
    const instance = createInstance(runtime, `
      new Vue({
        render: function (createElement) {
          return createElement('div', {}, [
            createElement('text', { attrs: { value: 'Hello' }}, []),
            createElement('foo', { props: { x: 'Weex' }})
          ])
        },
        components: {
          foo: {
            props: {
              x: { default: 'World' }
            },
            render: function (createElement) {
              return createElement('text', { attrs: { value: this.x }}, [])
            }
          }
        },
        el: "body"
      })
    `)
    expect(instance.getRealRoot()).toEqual({
      type: 'div',
      children: [
        { type: 'text', attr: { value: 'Hello' }},
        { type: 'text', attr: { value: 'Weex' }}
      ]
    })
  })

  it('should be generated with if/for diff', (done) => {
    const { render, staticRenderFns } = compileAndStringify(`
      <div>
        <text v-for="item in list" v-if="item.x">{{item.v}}</text>
      </div>
    `)
    const instance = createInstance(runtime, `
      new Vue({
        data: {
          list: [
            { v: 'Hello', x: true },
            { v: 'World', x: false },
            { v: 'Weex', x: true }
          ]
        },
        computed: {
          x: {
            get: function () { return 0 },
            set: function (v) {
              switch (v) {
                case 1:
                this.list[1].x = true
                break
                case 2:
                this.list.push({ v: 'v-if' })
                break
                case 3:
                this.list.push({ v: 'v-for', x: true })
                break
                case 4:
                this.list.splice(1, 2)
                break
              }
            }
          }
        },
        render: ${render},
        staticRenderFns: ${staticRenderFns},
        el: "body"
      })
    `)
    expect(instance.getRealRoot()).toEqual({
      type: 'div',
      children: [
        { type: 'text', attr: { value: 'Hello' }},
        { type: 'text', attr: { value: 'Weex' }}
      ]
    })

    syncPromise([
      checkRefresh(instance, { x: 1 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Hello' }},
            { type: 'text', attr: { value: 'World' }},
            { type: 'text', attr: { value: 'Weex' }}
          ]
        })
      }),
      checkRefresh(instance, { x: 2 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Hello' }},
            { type: 'text', attr: { value: 'World' }},
            { type: 'text', attr: { value: 'Weex' }}
          ]
        })
      }),
      checkRefresh(instance, { x: 3 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Hello' }},
            { type: 'text', attr: { value: 'World' }},
            { type: 'text', attr: { value: 'Weex' }},
            { type: 'text', attr: { value: 'v-for' }}
          ]
        })
      }),
      checkRefresh(instance, { x: 4 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Hello' }},
            { type: 'text', attr: { value: 'v-for' }}
          ]
        })
        done()
      })
    ])
  })

  it('should be generated with node structure diff', (done) => {
    const instance = createInstance(runtime, `
      new Vue({
        data: {
          counter: 0
        },
        render: function (createElement) {
          switch (this.counter) {
            case 1:
            return createElement('div', {}, [
              createElement('text', { attrs: { value: 'Hello' }}, []),
              createElement('text', { attrs: { value: 'World' }}, [])
            ])

            case 2:
            return createElement('div', {}, [
              createElement('text', { attrs: { value: 'Hello' }}, []),
              createElement('text', { attrs: { value: 'World' }}, []),
              createElement('text', { attrs: { value: 'Weex' }}, [])
            ])

            case 3:
            return createElement('div', {}, [
              createElement('text', { attrs: { value: 'Hello' }}, []),
              createElement('text', { attrs: { value: 'Weex' }}, [])
            ])

            case 4:
            return createElement('div', {}, [
              createElement('text', { attrs: { value: 'Weex' }}, [])
            ])

            case 5:
            return createElement('div', {}, [
              createElement('text', { attrs: { value: 'Hello' }}, []),
              createElement('text', { attrs: { value: 'Weex' }}, [])
            ])

            case 6:
            return createElement('div', {}, [
              createElement('input', { attrs: { value: 'Hello' }}, []),
              createElement('text', { attrs: { value: 'Weex' }}, [])
            ])

            default:
            return createElement('div', {}, [
              createElement('text', { attrs: { value: 'Hello' }}, []),
            ])
          }
        },
        el: "body"
      })
    `)
    expect(instance.getRealRoot()).toEqual({
      type: 'div',
      children: [
        { type: 'text', attr: { value: 'Hello' }}
      ]
    })

    syncPromise([
      checkRefresh(instance, { counter: 1 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Hello' }},
            { type: 'text', attr: { value: 'World' }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 2 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Hello' }},
            { type: 'text', attr: { value: 'World' }},
            { type: 'text', attr: { value: 'Weex' }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 3 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Hello' }},
            { type: 'text', attr: { value: 'Weex' }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 4 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Weex' }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 5 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Hello' }},
            { type: 'text', attr: { value: 'Weex' }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 6 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'input', attr: { value: 'Hello' }},
            { type: 'text', attr: { value: 'Weex' }}
          ]
        })
        done()
      })
    ])
  })

  it('should be generated with component diff', (done) => {
    const instance = createInstance(runtime, `
      new Vue({
        data: {
          counter: 0
        },
        components: {
          foo: {
            props: { a: { default: '1' }, b: { default: '2' }},
            render: function (createElement) {
              return createElement('text', { attrs: { value: this.a + '-' + this.b }}, [])
            }
          },
          bar: {
            render: function (createElement) {
              return createElement('text', { attrs: { value: 'Bar' }, style: { fontSize: 100 }})
            }
          },
          baz: {
            render: function (createElement) {
              return createElement('image', { attrs: { src: 'http://example.com/favicon.ico' }})
            }
          }
        },
        render: function (createElement) {
          switch (this.counter) {
            case 1:
            return createElement('div', {}, [
              createElement('foo', { props: { a: '111', b: '222' }}, [])
            ])

            case 2:
            return createElement('div', {}, [
              createElement('foo', {}, [])
            ])

            case 3:
            return createElement('div', {}, [
              createElement('bar', {}, [])
            ])

            case 4:
            return createElement('div', {}, [
              createElement('baz', {}, [])
            ])

            case 5:
            return createElement('div', {}, [
              createElement('foo', {}, []),
              createElement('bar', {}, []),
              createElement('baz', {}, [])
            ])

            default:
            return createElement('div', {}, [
              createElement('foo', { props: { a: '111' }}, [])
            ])
          }
        },
        el: "body"
      })
    `)
    expect(instance.getRealRoot()).toEqual({
      type: 'div',
      children: [
        { type: 'text', attr: { value: '111-2' }}
      ]
    })

    syncPromise([
      checkRefresh(instance, { counter: 1 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: '111-222' }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 2 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: '1-2' }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 3 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: 'Bar' }, style: { fontSize: 100 }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 4 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'image', attr: { src: 'http://example.com/favicon.ico' }}
          ]
        })
      }),
      checkRefresh(instance, { counter: 5 }, result => {
        expect(result).toEqual({
          type: 'div',
          children: [
            { type: 'text', attr: { value: '1-2' }},
            { type: 'text', attr: { value: 'Bar' }, style: { fontSize: 100 }},
            { type: 'image', attr: { src: 'http://example.com/favicon.ico' }}
          ]
        })
        done()
      })
    ])
  })
})
