/// <reference types="jest-enzyme" />
/// <reference path="../../../../../utilities/jest/extensions.ts" />

import * as fc from 'fast-check'
import * as Enzyme from 'enzyme'
import * as Axe from 'jest-axe'
import * as React from 'react'
import Index from '.'
import stringify from '../../utilities/stringify'

it('should exist', () => {
  expect(Index).toBeDefined()
})

it('should be a component', () => {
  expect(Index).toBeComponent()
})

it('should render without crashing given required props', () => {
  expect(() => <Index />).not.toThrow()
})

it('should render a base element to put interactive elements on', () => {
  const wrapper = Enzyme.shallow(<Index />)

  expect(wrapper.find('label')).toHaveLength(1)
})

it('should render an indicator as additional description for the content', () => {
  fc.assert(
    fc.property(fc.string(1, 20), (indicator) => {
      const wrapper = Enzyme.shallow(<Index indicator={indicator} />)

      expect(wrapper.find('div').childAt(1)).toHaveText(indicator)
    }),
  )
})

it('should render a hint for describing valid input values', () => {
  fc.assert(
    fc.property(fc.anything(), (label) => {
      const wrapper = Enzyme.shallow(<Index hint={label} />)

      const renderedLabel = stringify(label)

      if (renderedLabel.length > 0) {
        expect(wrapper.find('div').children()).toHaveLength(3)

        expect(wrapper.find('div').childAt(2)).toHaveText(`(${renderedLabel})`)
      } else {
        expect(wrapper.find('div').children()).toHaveLength(1)
      }
    }),
  )
})

it('should render a padding on the hint element when an indicator is present', () => {
  const wrapper = Enzyme.shallow(<Index hint="foo" indicator="foobar" />)

  expect(wrapper.find('div').childAt(3)).not.toHaveStyle('padding', '1rem')
})

it('should render as half-opaque when disabled', () => {
  const wrapper = Enzyme.shallow(<Index disabled />)

  expect(wrapper.find('div')).toHaveStyle('opacity', 0.5)
})

describe.each`
  multiline | tag
  ${false}  | ${'input'}
  ${true}   | ${'textarea'}
`('on multiline (multiline=$multiline)', ({ tag: rawTag, multiline: rawMultiline }) => {
  const tag = rawTag as string
  const multiline = rawMultiline as boolean

  it('should render an element to input text on', () => {
    const wrapper = Enzyme.shallow(<Index multiline={multiline} />)

    expect(wrapper.find('label').find(tag)).toHaveLength(1)
  })

  it('should render the rest of the passed props', () => {
    const wrapper = Enzyme.shallow(<Index multiline={multiline} placeholder="foo" />)

    expect(
      wrapper
        .find('label')
        .find(tag)
        .prop('placeholder'),
    ).toBe('foo')
  })

  it('should render a padding on the input element when an indicator is present', () => {
    const wrapper = Enzyme.shallow(<Index multiline={multiline} indicator="foobar" />)

    expect(
      wrapper
        .find('div')
        .find(tag)
        .prop('style')!.paddingRight,
    ).not.toBe('1rem')
  })
})

describe('on aiding user input', () => {
  it("should render a label to indicate the nature of the component's value", () => {
    const wrapper = Enzyme.shallow(<Index label="foo" />)

    expect(wrapper.find('label').find('span')).toHaveLength(1)
  })

  it('should render the label text', () => {
    fc.assert(
      fc.property(fc.anything(), (label) => {
        const wrapper = Enzyme.shallow(<Index label={label} />)

        expect(wrapper.find('label').find('span')).toHaveText(stringify(label))
      }),
      {
        numRuns: 300,
      },
    )
  })
})

it('should guarantee minimal accessibility', () => {
  fc.assert(
    fc.asyncProperty(fc.string(1, 20), async (s) => {
      const wrapper = Enzyme.mount(<Index label={s} />)
      const results = await Axe.axe(wrapper.getDOMNode())

      expect(results).toHaveNoViolations()
    }),
  )
})
