import Vue from 'vue'
import { compile } from 'web/compiler'
import { getAndRemoveAttr } from 'compiler/helpers'

describe('compile options', () => {
  it('should be compiled', () => {
    const { render, staticRenderFns, errors } = compile(`
      <div>
        <input type="text" v-model="msg" required max="8" v-validate:field1.group1.group2>
      </div>
    `, {
      directives: {
        validate (el, dir) {
          if (dir.name === 'validate' && dir.arg) {
            el.validate = {
              field: dir.arg,
              groups: dir.modifiers ? Object.keys(dir.modifiers) : []
            }
          }
        }
      },
      modules: [
        {
          transformNode (el) {
            el.validators = el.validators || []
            const validators = ['required', 'min', 'max', 'pattern', 'maxlength', 'minlength']
            validators.forEach(name => {
              const rule = getAndRemoveAttr(el, name)
              if (rule !== undefined) {
                el.validators.push({ name, rule })
              }
            })
          },
          genData (el) {
            let data = ''
            if (el.validate) {
              data += `validate:${JSON.stringify(el.validate)},`
            }
            if (el.validators) {
              data += `validators:${JSON.stringify(el.validators)},`
            }
            return data
          },
          transformCode (el, code) {
            // check
            if (!el.validate || !el.validators) {
              return code
            }
            // setup validation result props
            const result = { dirty: false } // define something other prop
            el.validators.forEach(validator => {
              result[validator.name] = null
            })
            // generate code
            return `_c('validate',{props:{
              field:${JSON.stringify(el.validate.field)},
              groups:${JSON.stringify(el.validate.groups)},
              validators:${JSON.stringify(el.validators)},
              result:${JSON.stringify(result)},
              child:${code}}
            })`
          }
        }
      ]
    })
    expect(render).not.toBeUndefined()
    expect(staticRenderFns).toEqual([])
    expect(errors).toEqual([])

    const renderFn = new Function(render)
    const vm = new Vue({
      data: {
        msg: 'hello'
      },
      components: {
        validate: {
          props: ['field', 'groups', 'validators', 'result', 'child'],
          render (h) {
            return this.child
          },
          computed: {
            valid () {
              let ret = true
              for (let i = 0; i < this.validators.length; i++) {
                const { name } = this.validators[i]
                if (!this.result[name]) {
                  ret = false
                  break
                }
              }
              return ret
            }
          },
          mounted () {
            // initialize validation
            const value = this.$el.value
            this.validators.forEach(validator => {
              const ret = this[validator.name](value, validator.rule)
              this.result[validator.name] = ret
            })
          },
          methods: {
            // something validators logic
            required (val) {
              return val.length > 0
            },
            max (val, rule) {
              return !(parseInt(val, 10) > parseInt(rule, 10))
            }
          }
        }
      },
      render: renderFn,
      staticRenderFns
    }).$mount()
    expect(vm.$el.innerHTML).toBe('<input type="text">')
    expect(vm.$children[0].valid).toBe(true)
  })

  it('should collect errors', () => {
    let compiled = compile('hello')
    expect(compiled.errors.length).toBe(1)
    expect(compiled.errors[0]).toContain('root element')

    compiled = compile('<div v-if="a----">{{ b++++ }}</div>')
    expect(compiled.errors.length).toBe(2)
    expect(compiled.errors[0]).toContain('Raw expression: v-if="a----"')
    expect(compiled.errors[1]).toContain('Raw expression: {{ b++++ }}')
  })

  it('should collect errors with source range', () => {
    let compiled = compile('hello', { outputSourceRange: true })
    expect(compiled.errors.length).toBe(1)
    expect(compiled.errors[0].start).toBe(0)
    expect(compiled.errors[0].end).toBeUndefined()

    compiled = compile('<div v-if="a----">{{ b++++ }}</div>', { outputSourceRange: true })
    expect(compiled.errors.length).toBe(2)
    expect(compiled.errors[0].start).toBe(5)
    expect(compiled.errors[0].end).toBe(17)
    expect(compiled.errors[1].start).toBe(18)
    expect(compiled.errors[1].end).toBe(29)

    compiled = compile('<div><span></div>', { outputSourceRange: true })
    expect(compiled.errors.length).toBe(1)
    expect(compiled.errors[0].start).toBe(5)
    expect(compiled.errors[0].end).toBe(11)
  })

  it('should collect source range for binding keys', () => {
    const compiled = compile('<div><slot v-bind:key="key" /></div>', { outputSourceRange: true })
    expect(compiled.errors.length).toBe(1)
    expect(compiled.errors[0].start).toBe(11)
    expect(compiled.errors[0].end).toBe(27)
  })
})
